Compare commits

..

No commits in common. "3f02bedd1d8e36bbb916fce12877a67041c94b32" and "a8efa8fc2b285e33614dbbd5bad99d23d06bf97e" have entirely different histories.

44 changed files with 1033 additions and 892 deletions

View File

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

View File

@ -1,15 +1,14 @@
package ch.zhaw.gartenverwaltung; package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.Inject; import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule; import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException; import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop; import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Pest; import ch.zhaw.gartenverwaltung.types.Pest;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task; import ch.zhaw.gartenverwaltung.types.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@ -21,20 +20,12 @@ import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class CropDetailController { public class CropDetailController {
private Crop crop; private Crop crop = null;
private final PlantListModel plantListModel = new PlantListModel();
@Inject private final TaskListModel taskListModel = new TaskListModel();
private PlantList plantList; private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
@Inject
private GardenSchedule gardenSchedule;
@Inject
private Garden garden;
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
@FXML @FXML
private ImageView imageView; private ImageView imageView;
@ -72,33 +63,33 @@ public class CropDetailController {
@FXML @FXML
private Label spacing_label; private Label spacing_label;
public CropDetailController() throws IOException {
}
@FXML @FXML
void editTaskList() { void editTaskList(ActionEvent event) {
} }
@FXML @FXML
void goBack() { void goBack(ActionEvent event) {
Stage stage = (Stage) imageView.getScene().getWindow(); Stage stage = (Stage) imageView.getScene().getWindow();
stage.close(); stage.close();
} }
@FXML @FXML
void setArea() { void setArea(ActionEvent event) {
} }
@FXML @FXML
void setLocation() { void setLocation(ActionEvent event) {
} }
public void setPlantFromCrop(Crop crop) throws PlantNotFoundException { public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException {
this.crop = crop; this.crop = crop;
try { 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()); cropName_label.setText(plant.name());
description_label.setText(plant.description()); description_label.setText(plant.description());
light_label.setText(String.valueOf(plant.light())); light_label.setText(String.valueOf(plant.light()));
@ -111,25 +102,14 @@ public class CropDetailController {
location_label.setText(""); location_label.setText("");
createTaskLists(crop); createTaskLists(crop);
createPestList(plant); createPestList(plant);
} catch (HardinessZoneNotSetException | IOException e) {
throw new PlantNotFoundException();
}
} }
private void createTaskLists(Crop crop) { private void createTaskLists(Crop crop) throws IOException {
crop.getCropId().ifPresent(id -> { List<Task> taskList = taskListModel.getTaskListForCrop(crop.getCropId().get());
List<Task> taskList;
try {
taskList = gardenSchedule.getTaskListForCrop(id);
for (Task task : taskList) { for (Task task : taskList) {
Label label = new Label(task.getDescription()); Label label = new Label(task.getDescription());
growthPhases_vbox.getChildren().add(label); 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) { private void createPestList(Plant plant) {

View File

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

View File

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

View File

@ -1,148 +0,0 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyGardenController {
private static final Logger LOG = Logger.getLogger(MyGardenController.class.getName());
@Inject
AppLoader appLoader;
@Inject
private Garden garden;
@Inject
private PlantList plantList;
@FXML
public AnchorPane myGardenRoot;
@FXML
private VBox myPlants_vbox;
@AfterInject
@SuppressWarnings("unused")
public void init() {
garden.getPlantedCrops().addListener((observable, oldValue, newValue) -> {
try {
createPlantView(newValue);
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
});
try {
createPlantView(garden.getPlantedCrops());
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
}
@FXML
void addPlant() {
myGardenRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "Plants.fxml"));
}
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
myPlants_vbox.getChildren().clear();
for (Crop crop : crops) {
HBox hBox = createPlantView(crop);
myPlants_vbox.getChildren().add(hBox);
}
}
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
//ToDo add better design
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
HBox hBox = new HBox(10);
ImageView imageView = new ImageView();
imageView.setPreserveRatio(false);
imageView.setFitHeight(100);
imageView.setFitWidth(100);
imageView.maxHeight(100);
if (plant.image() != null) {
imageView.setImage(plant.image());
}
hBox.setMinHeight(100);
Label label = new Label(plant.name());
label.setMaxWidth(2000);
HBox.setHgrow(label, Priority.ALWAYS);
Button details = new Button("Details");
Button delete = new Button("delete");
details.setOnAction(getGoToCropDetailEvent(crop));
delete.setOnAction(getDeleteCropEvent(crop));
hBox.getChildren().addAll(imageView, label, details, delete);
return hBox;
}
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
return (event) -> {
try {
Stage stage = new Stage();
if (appLoader.loadSceneToStage("CropDetail.fxml", stage) instanceof CropDetailController controller) {
controller.setPlantFromCrop(crop);
}
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(true);
stage.showAndWait();
} catch (IOException | PlantNotFoundException e) {
// TODO: show error alert
LOG.log(Level.SEVERE, "Could not load plant details.", e);
}
};
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
return (event) -> {
try {
showConfirmation(crop);
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
};
}
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Delete Crop");
alert.setHeaderText("Are you sure want to delete this Crop?");
alert.setContentText("Deleting this crop will remove all associated tasks from your schedule.");
alert.showAndWait()
.ifPresent(buttonType -> {
if (buttonType == ButtonType.OK) {
try {
garden.removeCrop(crop);
} catch (IOException e) {
// TODO: Show error alert
LOG.log(Level.SEVERE, "Could not remove crop.", e);
}
}
});
}
}

View File

@ -0,0 +1,160 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
public class MyPlantsController implements Initializable {
MainFXMLController mainController;
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
private final PlantListModel plantListModel = new PlantListModel();
@FXML
private VBox myPlants_vbox;
public MyPlantsController() throws IOException {
}
@FXML
void addPlant(ActionEvent event) throws IOException {
mainController.loadPane("Plants.fxml");
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
//ToDo update, when new crops are added
try {
loadCropList();
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
}
private void loadCropList() throws HardinessZoneNotSetException, IOException {
List<Crop> cropList = new LinkedList<>();
try {
cropList = getCropList();
} catch (IOException e) {
e.printStackTrace();
}
createPlantView(cropList);
}
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
myPlants_vbox.getChildren().clear();
for(Crop crop : crops) {
HBox hBox = createPlantView(crop);
myPlants_vbox.getChildren().add(hBox);
}
}
public void getMainController(MainFXMLController controller) {
mainController = controller;
}
private List<Crop> getCropList() throws IOException {
List<Crop> cropList;
cropList = gardenplanmodel.getCrops();
return cropList;
}
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
//ToDo add better design
Plant plant = plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0);
HBox hBox = new HBox(10);
ImageView imageView = new ImageView();
imageView.setPreserveRatio(false);
imageView.setFitHeight(100);
imageView.setFitWidth(100);
imageView.maxHeight(100);
if (plant.image() != null) {
imageView.setImage(plant.image());
}
hBox.setMinHeight(100);
Label label = new Label(plant.name());
label.setMaxWidth(2000);
HBox.setHgrow(label, Priority.ALWAYS);
Button details = new Button("Details");
Button delete = new Button("delete");
details.setOnAction(getGoToCropDetailEvent(crop));
delete.setOnAction(getDeleteCropEvent(crop));
hBox.getChildren().addAll(imageView, label, details, delete);
return hBox;
}
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Parent root;
FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("CropDetail.fxml")));
try {
root = fxmlLoader.load();
CropDetailController controller = fxmlLoader.getController();
controller.setPlantFromCrop(crop);
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(true);
stage.showAndWait();
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
}
};
return event;
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
try {
showConfirmation(crop);
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
}
};
return event;
}
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Delete Crop");
alert.setHeaderText("Are you sure want to delete this Crop?");
alert.setContentText("placeholder");
Optional<ButtonType> option = alert.showAndWait();
if (option.get() == ButtonType.OK) {
gardenplanmodel.removeCrop(crop);
loadCropList();
}
}
}

View File

@ -1,15 +1,18 @@
package ch.zhaw.gartenverwaltung; package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject; 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.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule; import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop; import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task; 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.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
@ -17,22 +20,19 @@ import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.ResourceBundle;
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 Crop selectedCrop = null;
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
private final PlantListModel plantListModel = new PlantListModel();
@Inject private final ListProperty<Crop> cropListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
private GardenSchedule gardenSchedule;
@Inject
private Garden garden;
@Inject
private PlantList plantList;
@FXML @FXML
private Label day1_label; private Label day1_label;
@ -82,11 +82,20 @@ public class MyScheduleController {
@FXML @FXML
private ListView<Crop> scheduledPlants_listview; private ListView<Crop> scheduledPlants_listview;
@AfterInject public MyScheduleController() throws IOException {
@SuppressWarnings("unused") }
public void init() {
@Override
public void initialize(URL location, ResourceBundle resources) {
List<Crop> cropList;
try {
cropList = gardenplanmodel.getCrops();
cropListProperty.addAll(cropList);
} catch (IOException e) {
e.printStackTrace();
}
setCellFactoryListView(); setCellFactoryListView();
scheduledPlants_listview.itemsProperty().bind(garden.getPlantedCrops()); scheduledPlants_listview.itemsProperty().bind(cropListProperty);
lookForSelectedListEntries(); lookForSelectedListEntries();
setDayLabels(); setDayLabels();
information_label.setText(""); information_label.setText("");
@ -98,13 +107,16 @@ public class MyScheduleController {
} }
private void lookForSelectedListEntries() { private void lookForSelectedListEntries() {
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Crop>() {
@Override
public void changed(ObservableValue<? extends Crop> observable, Crop oldValue, Crop newValue) {
selectedCrop = newValue; selectedCrop = newValue;
try { try {
loadTaskList(); loadTaskList();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
}
}); });
} }
@ -120,7 +132,7 @@ public class MyScheduleController {
} }
private void setCellFactoryListView() { private void setCellFactoryListView() {
scheduledPlants_listview.setCellFactory(param -> new ListCell<>() { scheduledPlants_listview.setCellFactory(param -> new ListCell<Crop>() {
@Override @Override
protected void updateItem(Crop crop, boolean empty) { protected void updateItem(Crop crop, boolean empty) {
super.updateItem(crop, empty); super.updateItem(crop, empty);
@ -129,12 +141,9 @@ public class MyScheduleController {
setText(null); setText(null);
} else { } else {
try { try {
String text = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()) setText(plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0).name());
.map(Plant::name)
.orElse("");
setText(text);
} catch (HardinessZoneNotSetException | IOException e) { } catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.WARNING, "Could not get plant for Cell", e); e.printStackTrace();
} }
} }
} }
@ -142,11 +151,11 @@ public class MyScheduleController {
} }
private void loadTaskList() throws IOException { private void loadTaskList() throws IOException {
List<List<Task>> taskLists; List<List<Task>> taskLists = new LinkedList<>();
if (selectedCrop != null) { if (selectedCrop != null) {
taskLists = gardenSchedule.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get()); taskLists = taskListModel.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get());
} else { } else {
taskLists = gardenSchedule.getTasksUpcomingWeek(); taskLists = taskListModel.getTasksUpcomingWeek();
} }
if (!taskLists.isEmpty()) { if (!taskLists.isEmpty()) {
viewTaskListOfDay(day1_pane, taskLists.get(0)); viewTaskListOfDay(day1_pane, taskLists.get(0));

View File

@ -1,52 +1,44 @@
package ch.zhaw.gartenverwaltung; package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.Garden; import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Seasons; import ch.zhaw.gartenverwaltung.types.Seasons;
import javafx.beans.property.ListProperty; import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty; import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.net.URL;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class PlantsController { public class PlantsController implements Initializable {
private static final Logger LOG = Logger.getLogger(PlantsController.class.getName()); 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 Plant selectedPlant = null;
private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A; private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A;
// TODO: move to model // TODO: move to model
private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@FXML
public AnchorPane plantsRoot;
@FXML @FXML
private VBox seasons; private VBox seasons;
@ -70,34 +62,20 @@ public class PlantsController {
/** /**
* open new window to select sow or harvest day to save the crop * open new window to select sow or harvest day to save the crop
* @param event event
*/ */
@FXML @FXML
void selectSowDate() throws IOException { void selectSowDate(ActionEvent event) throws IOException {
Dialog<LocalDate> dateSelection = new Dialog<>(); Parent root;
dateSelection.setTitle("Select Date"); FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("SelectSowDay.fxml")));
dateSelection.setHeaderText(String.format("Select Harvest/Sow Date for %s:", selectedPlant.name())); root = fxmlLoader.load();
dateSelection.setResizable(false); SelectSowDayController controller = fxmlLoader.getController();
controller.getSelectedPlant(selectedPlant);
DialogPane dialogPane = dateSelection.getDialogPane(); Stage stage = new Stage();
stage.setScene(new Scene(root));
ButtonType sowButton = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE); stage.initModality(Modality.APPLICATION_MODAL);
dialogPane.getButtonTypes().addAll(sowButton, ButtonType.CANCEL); stage.setResizable(false);
stage.showAndWait();
if (appLoader.loadPaneToDialog("SelectSowDay.fxml", dialogPane) instanceof SelectSowDayController controller) {
controller.initSaveButton((Button) dialogPane.lookupButton(sowButton));
controller.setSelectedPlant(selectedPlant);
dateSelection.setResultConverter(button -> button.equals(sowButton) ? controller.retrieveResult() : null);
dateSelection.showAndWait()
.ifPresent(date -> {
try {
garden.plantAsCrop(selectedPlant, date);
} catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
LOG.log(Level.SEVERE, "Couldn't save Crop", e);
}
plantsRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "MyGarden.fxml"));
});
}
} }
/** /**
@ -107,9 +85,8 @@ public class PlantsController {
* create event listener for selected list entry and search by query * create event listener for selected list entry and search by query
* {@inheritDoc} * {@inheritDoc}
*/ */
@AfterInject @Override
@SuppressWarnings("unused") public void initialize(URL url, ResourceBundle resourceBundle) {
public void init() {
setListCellFactory(); setListCellFactory();
fillPlantListWithHardinessZone(); fillPlantListWithHardinessZone();
list_plants.itemsProperty().bind(plantListProperty); list_plants.itemsProperty().bind(plantListProperty);
@ -120,7 +97,6 @@ public class PlantsController {
createFilterSeasons(); createFilterSeasons();
createFilterHardinessZone(); createFilterHardinessZone();
lookForSelectedListEntry(); lookForSelectedListEntry();
try { try {
viewFilteredListBySearch(); viewFilteredListBySearch();
} catch (HardinessZoneNotSetException e) { } catch (HardinessZoneNotSetException e) {
@ -151,7 +127,6 @@ public class PlantsController {
/** /**
* get plant list according to param season and hardiness zone * get plant list according to param season and hardiness zone
* fill list view with plant list * fill list view with plant list
*
* @param season enum of seasons * @param season enum of seasons
* @throws HardinessZoneNotSetException throws exception * @throws HardinessZoneNotSetException throws exception
* @throws IOException throws exception * @throws IOException throws exception
@ -164,7 +139,6 @@ public class PlantsController {
/** /**
* get plant list filtered by search plant entry and hardiness zone * get plant list filtered by search plant entry and hardiness zone
* fill list view with plant list * fill list view with plant list
*
* @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined * @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
* @throws IOException throws exception * @throws IOException throws exception
*/ */
@ -211,7 +185,7 @@ public class PlantsController {
for (HardinessZone zone : HardinessZone.values()) { for (HardinessZone zone : HardinessZone.values()) {
RadioButton radioButton = new RadioButton(zone.name()); RadioButton radioButton = new RadioButton(zone.name());
radioButton.setToggleGroup(hardinessGroup); radioButton.setToggleGroup(hardinessGroup);
radioButton.setPadding(new Insets(0, 0, 10, 0)); radioButton.setPadding(new Insets(0,0,10,0));
if (zone.equals(DEFAULT_HARDINESS_ZONE)) { if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
radioButton.setSelected(true); radioButton.setSelected(true);
} }
@ -233,7 +207,7 @@ public class PlantsController {
for (Seasons season : Seasons.values()) { for (Seasons season : Seasons.values()) {
RadioButton radioButton = new RadioButton(season.getName()); RadioButton radioButton = new RadioButton(season.getName());
radioButton.setToggleGroup(seasonGroup); radioButton.setToggleGroup(seasonGroup);
radioButton.setPadding(new Insets(0, 0, 10, 0)); radioButton.setPadding(new Insets(0,0,10,0));
if (season.equals(Seasons.AllSEASONS)) { if (season.equals(Seasons.AllSEASONS)) {
radioButton.setSelected(true); radioButton.setSelected(true);
} }
@ -267,12 +241,12 @@ public class PlantsController {
Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png"))); Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
img_plant.setImage(img); img_plant.setImage(img);
list_plants.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { list_plants.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) { if(newValue != null) {
selectedPlant = newValue; selectedPlant = newValue;
description_plant.setText(selectedPlant.description()); description_plant.setText(selectedPlant.description());
selectSowDay_button.setDisable(false); selectSowDay_button.setDisable(false);
Image img1; Image img1;
if (selectedPlant.image() != null) { if(selectedPlant.image() != null) {
img1 = selectedPlant.image(); img1 = selectedPlant.image();
} else { } else {
img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png"))); img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));

View File

@ -1,94 +1,174 @@
package ch.zhaw.gartenverwaltung; 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.GrowthPhaseType;
import ch.zhaw.gartenverwaltung.types.Plant; 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.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.stage.Stage;
import javafx.util.Callback; import javafx.util.Callback;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List;
import java.util.ResourceBundle;
public class SelectSowDayController { public class SelectSowDayController implements Initializable {
private Plant selectedPlant; private Plant selectedPlant = null;
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
@FXML @FXML
private DatePicker datepicker; private DatePicker datepicker;
@FXML @FXML
private RadioButton harvest_radio; private Label popup_label;
@FXML
private Button save_button;
@FXML @FXML
private RadioButton sow_radio; private RadioButton sow_radio;
@FXML
public ToggleGroup phase_group;
public LocalDate retrieveResult() { public SelectSowDayController() throws IOException {}
LocalDate sowDate = datepicker.getValue();
if (harvest_radio.isSelected()) { /**
sowDate = selectedPlant.sowDateFromHarvestDate(sowDate); * close the date selector window
} * @param event event
return sowDate; */
@FXML
void cancel(ActionEvent event) {
closeWindow();
} }
/** /**
* Set the {@link Plant} for which a date should be selected. * get sow date from datePicker or calculate sow date from harvest date
* * save selected plant and sow date
* @param event event
*/
@FXML
void save(ActionEvent event) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
LocalDate sowDate;
if (sow_radio.isSelected()) {
sowDate = datepicker.getValue();
} else {
//ToDo method to get current lifecycle group in plant
sowDate = selectedPlant.sowDateFromHarvestDate(datepicker.getValue(), 0);
}
gardenplanmodel.plantAsCrop(selectedPlant, sowDate);
closeWindow();
}
/**
* save the plant which will be planted and update label
* @param plant Plant * @param plant Plant
*/ */
public void setSelectedPlant(Plant plant) { public void getSelectedPlant(Plant plant) {
selectedPlant = plant; selectedPlant = plant;
popup_label.setText("Select Harvest/Sow Date for" + selectedPlant.name());
} }
/** /**
* add listener and set default values * add listener and set default values
* {@inheritDoc}
* @param location location
* @param resources resources
*/ */
@FXML @Override
public void initialize() { public void initialize(URL location, ResourceBundle resources) {
clearDatePickerEntries(); clearDatePickerEntries();
Callback<DatePicker, DateCell> dayCellFactory = getDayCellFactory(); Callback<DatePicker, DateCell> dayCellFactory= getDayCellFactory();
datepicker.setDayCellFactory(dayCellFactory); datepicker.setDayCellFactory(dayCellFactory);
datepicker.setEditable(false); datepicker.getEditor().setEditable(false);
sow_radio.setUserData(GrowthPhaseType.SOW); enableDisableSaveButton();
harvest_radio.setUserData(GrowthPhaseType.HARVEST);
}
public void initSaveButton(Button saveButton) {
saveButton.disableProperty().bind(datepicker.valueProperty().isNull());
} }
/** /**
* clear date picker editor when radio button is changed * clear date picker editor when radio button is changed
*/ */
private void clearDatePickerEntries() { private void clearDatePickerEntries() {
harvest_radio.selectedProperty().addListener((observable, oldValue, isNowSelected) -> datepicker.setValue(null)); sow_radio.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean isNowSelected) {
datepicker.getEditor().clear();
}
});
} }
/** /**
* date picker disable/enable dates according to selected plant: sow or harvest day * date picker disable/enable dates according to selected plant: sow or harvest day
*
* @return cellFactory of datePicker * @return cellFactory of datePicker
*/ */
private Callback<DatePicker, DateCell> getDayCellFactory() { private Callback<DatePicker, DateCell> getDayCellFactory() {
return (datePicker) -> new DateCell() { final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
private final LocalDate today = LocalDate.now();
@Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
@Override @Override
public void updateItem(LocalDate item, boolean empty) { public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
setDisable(true); setDisable(true);
setStyle("-fx-background-color: #ffc0cb;"); setStyle("-fx-background-color: #ffc0cb;");
List<LocalDate> dates;
if (item.compareTo(today) > 0 && (!harvest_radio.isSelected() || selectedPlant.sowDateFromHarvestDate(item).compareTo(today) >= 0)) { LocalDate today = LocalDate.now();
GrowthPhaseType selectedPhase = (GrowthPhaseType) phase_group.getSelectedToggle().getUserData(); if (sow_radio.isSelected()) {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW);
if (selectedPlant.isDateInPhase(item, selectedPhase)) { } 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); setDisable(false);
setStyle("-fx-background-color: #32CD32;"); setStyle("-fx-background-color: #32CD32;");
} }
} }
if ((!sow_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) {
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
}
} }
}; };
} }
};
return dayCellFactory;
}
/**
* close date picker window
*/
private void closeWindow() {
Stage stage = (Stage) save_button.getScene().getWindow();
stage.close();
}
/**
* disable save button, when there is no date selected in date picker
*/
private void enableDisableSaveButton() {
save_button.setDisable(true);
datepicker.getEditor().textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue == null || newValue.equals("")) {
save_button.setDisable(true);
} else {
save_button.setDisable(false);
}
}
});
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,41 +1,41 @@
package ch.zhaw.gartenverwaltung.models; package ch.zhaw.gartenverwaltung.gardenplan;
import ch.zhaw.gartenverwaltung.io.CropList; import ch.zhaw.gartenverwaltung.io.GardenPlan;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonGardenPlan;
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop; import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task; import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
/** /**
* The Gardenplan model manages the crops in the gardenplan. * The Gardenplan model manages the crops in the gardenplan.
*/ */
public class Garden { public class Gardenplanmodel {
private final CropList cropList; private GardenPlan gardenPlan;
private final ListProperty<Crop> plantedCrops = new SimpleListProperty<>(FXCollections.observableArrayList()); private List<Crop> cropList;
private final GardenSchedule gardenSchedule; private TaskListModel taskListModel;
private Object IllegalArgumentException;
/** /**
* Constructor of Gardenplan model * Constructor of Gardenplan model
* *
* @param gardenSchedule holds a reference to the task list object. * @param taskListModel holds a reference to the task list object.
*/ */
public Garden(GardenSchedule gardenSchedule, CropList cropList) throws IOException { public Gardenplanmodel(TaskListModel taskListModel) throws IOException {
this.gardenSchedule = gardenSchedule; this.taskListModel = taskListModel;
this.cropList = cropList; gardenPlan = new JsonGardenPlan();
plantedCrops.addAll(cropList.getCrops()); cropList = new ArrayList<>();
} cropList = gardenPlan.getCrops();
public ListProperty<Crop> getPlantedCrops() {
return plantedCrops;
} }
/** /**
@ -52,10 +52,9 @@ public class Garden {
Crop crop = new Crop(plant.id(), plantingDate); Crop crop = new Crop(plant.id(), plantingDate);
//TODO Add Area to Plant //TODO Add Area to Plant
//crop.withArea(0); //crop.withArea(0);
cropList.saveCrop(crop); gardenPlan.saveCrop(crop);
gardenSchedule.planTasksForCrop(crop); taskListModel.planTasksForCrop(crop);
plantedCrops.clear(); cropList = gardenPlan.getCrops();
plantedCrops.addAll(cropList.getCrops());
} }
/** /**
@ -65,10 +64,9 @@ public class Garden {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void removeCrop(Crop crop) throws IOException { public void removeCrop(Crop crop) throws IOException {
cropList.removeCrop(crop); gardenPlan.removeCrop(crop);
gardenSchedule.removeTasksForCrop(crop.getCropId().orElseThrow(IllegalArgumentException::new)); taskListModel.removeTasksForCrop(crop.getCropId().orElseThrow(IllegalArgumentException::new));
plantedCrops.clear(); cropList = gardenPlan.getCrops();
plantedCrops.addAll(cropList.getCrops());
} }
/** /**
* Returns a list of {@link Crop}s which are currently in the gardenplan. * Returns a list of {@link Crop}s which are currently in the gardenplan.
@ -76,7 +74,10 @@ public class Garden {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public List<Crop> getCrops() throws IOException { public List<Crop> getCrops() throws IOException {
return cropList.getCrops(); if(!cropList.isEmpty()){
cropList = gardenPlan.getCrops();
}
return cropList;
} }
/** /**
@ -86,6 +87,6 @@ public class Garden {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public Optional<Crop> getCrop(Long cropId) throws IOException { public Optional<Crop> getCrop(Long cropId) throws IOException {
return cropList.getCropById(cropId); return gardenPlan.getCropById(cropId);
} }
} }

View File

@ -6,7 +6,7 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
public interface CropList { public interface GardenPlan {
/** /**
* Yields a list of all {@link Crop}s in the database. * Yields a list of all {@link Crop}s in the database.
* *

View File

@ -19,7 +19,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
public class JsonCropList implements CropList { public class JsonGardenPlan implements GardenPlan {
private final URL dataSource; private final URL dataSource;
private IdProvider idProvider; private IdProvider idProvider;
@ -42,7 +42,7 @@ public class JsonCropList implements CropList {
/** /**
* Default constructor * Default constructor
*/ */
public JsonCropList() { public JsonGardenPlan() {
this.dataSource = getClass().getResource("user-crops.json"); this.dataSource = getClass().getResource("user-crops.json");
} }
@ -50,10 +50,9 @@ public class JsonCropList implements CropList {
* Constructor to use a specified {@link URL} as a {@link #dataSource} * 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 * @param dataSource A {@link URL} to the file to be used as a data source
*/ */
public JsonCropList(URL dataSource) { public JsonGardenPlan(URL dataSource) {
this.dataSource = dataSource; this.dataSource = dataSource;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -20,12 +20,12 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
/** /**
* Implements the {@link PlantList} interface for loading {@link Plant} objects * Implements the {@link PlantDatabase} interface for loading {@link Plant} objects
* from a JSON file. * from a JSON file.
* The reads are cached to minimize file-io operations. * The reads are cached to minimize file-io operations.
*/ */
public class JsonPlantList implements PlantList { public class JsonPlantDatabase implements PlantDatabase {
private final URL dataSource; private final URL dataSource = getClass().getResource("plantdb.json");
private HardinessZone currentZone; private HardinessZone currentZone;
private Map<Long, Plant> plantMap = Collections.emptyMap(); private Map<Long, Plant> plantMap = Collections.emptyMap();
@ -44,19 +44,12 @@ public class JsonPlantList implements PlantList {
imageModule.addDeserializer(Image.class, new PlantImageDeserializer()); imageModule.addDeserializer(Image.class, new PlantImageDeserializer());
} }
public JsonPlantList() {
this.dataSource = getClass().getResource("plantdb.json");
}
public JsonPlantList(URL dataSource) {
this.dataSource = dataSource;
}
/** /**
* If no data is currently loaded, or the specified zone differs * If no data is currently loaded, or the specified zone differs
* from the {@link #currentZone}, data is loaded from {@link #dataSource}. * from the {@link #currentZone}, data is loaded from {@link #dataSource}.
* In any case, the values of {@link #plantMap} are returned. * In any case, the values of {@link #plantMap} are returned.
* *
* @see PlantList#getPlantList(HardinessZone) * @see PlantDatabase#getPlantList(HardinessZone)
*/ */
@Override @Override
public List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException { public List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException {
@ -67,7 +60,7 @@ public class JsonPlantList implements PlantList {
} }
/** /**
* @see PlantList#getPlantById(long) * @see PlantDatabase#getPlantById(long)
*/ */
@Override @Override
public Optional<Plant> getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException { public Optional<Plant> getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException {

View File

@ -1,5 +1,6 @@
package ch.zhaw.gartenverwaltung.io; package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Task; import ch.zhaw.gartenverwaltung.types.Task;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
@ -19,13 +20,13 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Implements the {@link TaskList} interface for loading and writing {@link Task} objects * Implements the {@link TaskDatabase} interface for loading and writing {@link Task} objects
* from and to a JSON file. * from and to a JSON file.
* The reads are cached to minimize file-io operations. * The reads are cached to minimize file-io operations.
*/ */
public class JsonTaskList implements TaskList { public class JsonTaskDatabase implements TaskDatabase{
IdProvider idProvider; IdProvider idProvider;
private final URL dataSource; private final URL dataSource = getClass().getResource("taskdb.json");
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!"; private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
private Map<Long, Task> taskMap = Collections.emptyMap(); private Map<Long, Task> taskMap = Collections.emptyMap();
@ -43,18 +44,11 @@ public class JsonTaskList implements TaskList {
timeModule.addSerializer(LocalDate.class, dateSerializer); timeModule.addSerializer(LocalDate.class, dateSerializer);
} }
public JsonTaskList() {
this.dataSource = getClass().getResource("taskdb.json");
}
public JsonTaskList(URL dataSource) {
this.dataSource = dataSource;
}
/** /**
* If no data is currently loaded, data is loaded from {@link #dataSource}. * If no data is currently loaded, data is loaded from {@link #dataSource}.
* In any case, the values of {@link #taskMap} are returned. * In any case, the values of {@link #taskMap} are returned.
* *
* @see TaskList#getTaskList(LocalDate, LocalDate) * @see TaskDatabase#getTaskList(LocalDate, LocalDate)
*/ */
@Override @Override
public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{ public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{
@ -86,13 +80,7 @@ public class JsonTaskList implements TaskList {
if(taskMap.isEmpty()) { if(taskMap.isEmpty()) {
loadTaskListFromFile(); loadTaskListFromFile();
} }
taskMap.values().removeIf(task -> task.getCropId() == cropId);
List<Task> temptasks = taskMap.values().stream().
filter(task -> { return task.getCropId() == cropId;}).toList();
for (Task task : temptasks ) {
taskMap.remove(task.getId());
}
writeTaskListToFile();
} }
/** /**
@ -102,7 +90,7 @@ public class JsonTaskList implements TaskList {
* it to the {@link #taskMap}. In any case, the {@link #taskMap} is written * it to the {@link #taskMap}. In any case, the {@link #taskMap} is written
* to the {@link #dataSource}. * to the {@link #dataSource}.
* *
* @see TaskList#saveTask(Task) * @see TaskDatabase#saveTask(Task)
*/ */
@Override @Override
public void saveTask(Task task) throws IOException { public void saveTask(Task task) throws IOException {
@ -112,7 +100,6 @@ public class JsonTaskList implements TaskList {
if(task.getId() == 0) { if(task.getId() == 0) {
task.withId(idProvider.incrementAndGet()); task.withId(idProvider.incrementAndGet());
} }
taskMap.put(task.getId(),task);
writeTaskListToFile(); writeTaskListToFile();
} }
@ -121,7 +108,7 @@ public class JsonTaskList implements TaskList {
* If the {@link Task}s id is found in the {@link #taskMap}, the Task is removed * If the {@link Task}s id is found in the {@link #taskMap}, the Task is removed
* from the {@link #taskMap}. Then the Task are written to the {@link #dataSource}. * from the {@link #taskMap}. Then the Task are written to the {@link #dataSource}.
* *
* @see TaskList#removeTask(Task) * @see TaskDatabase#removeTask(Task)
*/ */
@Override @Override
public void removeTask(Task task) throws IOException { public void removeTask(Task task) throws IOException {

View File

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

View File

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

View File

@ -1,6 +1,6 @@
package ch.zhaw.gartenverwaltung.json; package ch.zhaw.gartenverwaltung.json;
import ch.zhaw.gartenverwaltung.io.PlantList; import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
@ -18,7 +18,7 @@ public class PlantImageDeserializer extends JsonDeserializer<Image> {
@Override @Override
public Image deserialize(JsonParser parser, DeserializationContext context) throws IOException { public Image deserialize(JsonParser parser, DeserializationContext context) throws IOException {
Image result = null; Image result = null;
URL imageUrl = PlantList.class.getResource(String.format("images/%s", parser.getText())); URL imageUrl = PlantDatabase.class.getResource(String.format("images/%s", parser.getText()));
if (imageUrl != null) { if (imageUrl != null) {
try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) { try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) {
result = new Image(is); result = new Image(is);

View File

@ -1,7 +1,8 @@
package ch.zhaw.gartenverwaltung.models; package ch.zhaw.gartenverwaltung.plantList;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.PlantList; import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase;
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType; import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
@ -15,7 +16,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class PlantListModel { public class PlantListModel {
private final PlantList plantList; private PlantDatabase plantDatabase;
private HardinessZone currentZone; private HardinessZone currentZone;
/** /**
@ -27,13 +28,18 @@ public class PlantListModel {
/** /**
* Constructor to create Database Object. * Constructor to create Database Object.
*/ */
public PlantListModel(PlantList plantList) { public PlantListModel() {
this.plantList = plantList; plantDatabase = new JsonPlantDatabase();
setDefaultZone();
}
public PlantListModel(PlantDatabase plantDatabase) {
this.plantDatabase = plantDatabase;
setDefaultZone(); setDefaultZone();
} }
private void setDefaultZone() { private void setDefaultZone() {
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Settings currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config
} }
public void setCurrentZone(HardinessZone currentZone) { public void setCurrentZone(HardinessZone currentZone) {
@ -66,7 +72,7 @@ public class PlantListModel {
*/ */
public List<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> comparator) throws HardinessZoneNotSetException, IOException { public List<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> comparator) throws HardinessZoneNotSetException, IOException {
setCurrentZone(zone); setCurrentZone(zone);
return plantList.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList()); return plantDatabase.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList());
} }
/** /**
@ -95,7 +101,7 @@ public class PlantListModel {
public List<Plant> getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException { public List<Plant> getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException {
setCurrentZone(zone); setCurrentZone(zone);
List<Plant> plantList = new ArrayList<>(); List<Plant> plantList = new ArrayList<>();
this.plantList.getPlantById(zone, id).ifPresent(plantList::add); plantDatabase.getPlantById(zone, id).ifPresent(plantList::add);
return plantList; return plantList;
} }

View File

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

View File

@ -1,6 +1,6 @@
package ch.zhaw.gartenverwaltung.models; package ch.zhaw.gartenverwaltung.taskList;
import ch.zhaw.gartenverwaltung.Settings; import ch.zhaw.gartenverwaltung.Config;
import ch.zhaw.gartenverwaltung.io.*; import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.types.*; import ch.zhaw.gartenverwaltung.types.*;
@ -9,23 +9,29 @@ import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class GardenSchedule { public class TaskListModel {
private final TaskList taskList; private TaskDatabase taskDatabase;
private final PlantList plantList; private PlantDatabase plantDatabase;
/** /**
* Comparators to create sorted Task List * Comparators to create sorted Task List
*/ */
static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate); static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate);
public TaskListModel(){
taskDatabase = new JsonTaskDatabase();
plantDatabase = new JsonPlantDatabase();
}
/** /**
* Constructor to create Database Objects. * Constructor to create Database Objects.
*/ */
public GardenSchedule(TaskList taskList, PlantList plantList) { public TaskListModel(TaskDatabase taskDatabase, PlantDatabase plantDatabase) {
this.taskList = taskList; this.taskDatabase = taskDatabase;
this.plantList = plantList; this.plantDatabase = plantDatabase;
} }
/** /**
@ -34,7 +40,7 @@ public class GardenSchedule {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void addTask(Task task) throws IOException { public void addTask(Task task) throws IOException {
taskList.saveTask(task); taskDatabase.saveTask(task);
} }
/** /**
@ -45,7 +51,7 @@ public class GardenSchedule {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void planTasksForCrop(Crop crop) throws PlantNotFoundException, HardinessZoneNotSetException, IOException { public void planTasksForCrop(Crop crop) throws PlantNotFoundException, HardinessZoneNotSetException, IOException {
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).orElseThrow(PlantNotFoundException::new); Plant plant = plantDatabase.getPlantById(Config.getCurrentHardinessZone(), crop.getPlantId()).orElseThrow(PlantNotFoundException::new);
for (GrowthPhase growthPhase : plant.lifecycle()) { for (GrowthPhase growthPhase : plant.lifecycle()) {
for (TaskTemplate taskTemplate : growthPhase.taskTemplates()) { for (TaskTemplate taskTemplate : growthPhase.taskTemplates()) {
addTask(taskTemplate.generateTask(crop.getStartDate(), crop.getCropId().orElse(0L))); addTask(taskTemplate.generateTask(crop.getStartDate(), crop.getCropId().orElse(0L)));
@ -59,7 +65,7 @@ public class GardenSchedule {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void removeTasksForCrop(long cropId) throws IOException { public void removeTasksForCrop(long cropId) throws IOException {
taskList.removeTasksForCrop(cropId); taskDatabase.removeTasksForCrop(cropId);
} }
/** /**
@ -68,7 +74,7 @@ public class GardenSchedule {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void removeTask(Task task) throws IOException { public void removeTask(Task task) throws IOException {
taskList.removeTask(task); taskDatabase.removeTask(task);
} }
private List<Task> filterListByCrop(List<Task> taskList, Long cropId) { private List<Task> filterListByCrop(List<Task> taskList, Long cropId) {
@ -138,7 +144,7 @@ public class GardenSchedule {
List<List<Task>> dayTaskList = new ArrayList<>(); List<List<Task>> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) { for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i); LocalDate date = LocalDate.now().plusDays(i);
dayTaskList.add(taskList.getTaskList(date, date)); dayTaskList.add(taskDatabase.getTaskList(date, date));
} }
return dayTaskList; return dayTaskList;
} }
@ -152,7 +158,7 @@ public class GardenSchedule {
List<List<Task>> dayTaskList = new ArrayList<>(); List<List<Task>> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) { for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i); LocalDate date = LocalDate.now().plusDays(i);
dayTaskList.add(filterListByCrop(taskList.getTaskList(date, date), cropId)); dayTaskList.add(filterListByCrop(taskDatabase.getTaskList(date, date), cropId));
} }
return dayTaskList; return dayTaskList;
} }
@ -165,7 +171,7 @@ public class GardenSchedule {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public List<Task> getFilteredTaskList(LocalDate start, LocalDate end) throws IOException { public List<Task> getFilteredTaskList(LocalDate start, LocalDate end) throws IOException {
return getSortedTaskList(taskList.getTaskList(start, end), sortByStartDate); return getSortedTaskList(taskDatabase.getTaskList(start, end), sortByStartDate);
} }
/** /**

View File

@ -3,7 +3,7 @@ package ch.zhaw.gartenverwaltung.types;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.MonthDay; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -42,10 +42,11 @@ public record Plant(
/** /**
* get sow date from given harvest day from lifecycle group * get sow date from given harvest day from lifecycle group
* @param harvestDate date of the harvest * @param harvestDate date of the harvest
* @param group lifecycle group
* @return LocaleDate of sow date * @return LocaleDate of sow date
*/ */
public LocalDate sowDateFromHarvestDate(LocalDate harvestDate) { public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) {
return harvestDate.minusDays(timeToHarvest(lifecycleGroupFromHarvestDate(harvestDate))); return harvestDate.minusDays(timeToHarvest(group));
} }
/** /**
@ -68,43 +69,37 @@ public record Plant(
return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear)); return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear));
} }
public int lifecycleGroupFromHarvestDate(LocalDate harvestDate) { /**
return lifecycle.stream() * filter out the given growthPhase out of the lifecycle
.filter(growthPhase -> growthPhase.type().equals(GrowthPhaseType.HARVEST) && * create list of dates for this growthPhase and return it
dateInRange(harvestDate, growthPhase.startDate(), growthPhase.endDate())) * @param growthPhase the wanted growthPhase
.map(GrowthPhase::group) * @return a list of dates of the current year
.findFirst() */
.orElse(0); public List<LocalDate> getDateListOfGrowthPhase(GrowthPhaseType growthPhase) {
List<LocalDate> dates = new LinkedList<>();
for (GrowthPhase growth : lifecycle) {
if (growth.type().equals(growthPhase)) {
dates = addDatesFromMonthDay(growth);
}
}
return dates;
} }
/** /**
* Checks if the given {@link LocalDate} is within a {@link GrowthPhase} of the given {@link GrowthPhaseType} * transform monthDay value of the given growthPhase to localDate
* * return a list of dates from start to end of growth phase
* @param date The date to check. * @param growthPhase the current growthPhase
* @param phase The {@link GrowthPhaseType} to match against * @return a list of dates of the current year
* @return Whether the date is within the given {@link GrowthPhaseType}
*/ */
public boolean isDateInPhase(LocalDate date, GrowthPhaseType phase) { private List<LocalDate> addDatesFromMonthDay(GrowthPhase growthPhase) {
return lifecycle.stream() List<LocalDate> dates = new LinkedList<>();
.filter(growthPhase -> growthPhase.type().equals(phase)) LocalDate today = LocalDate.now();
.anyMatch(growthPhase -> dateInRange(date, growthPhase.startDate(), growthPhase.endDate())); LocalDate start = growthPhase.startDate().atYear(today.getYear());
LocalDate end = growthPhase.endDate().atYear(today.getYear());
while (!start.isAfter(end)) {
dates.add(start);
start = start.plusDays(1);
} }
return dates;
/**
* Checks if the given {@link LocalDate} is within the given {@link MonthDay} range.
* (regardless of year)
*
* @param subject The date to check
* @param min The start of the date-range
* @param max The end of the date-range
* @return Whether the subject is within the range.
*/
private boolean dateInRange(LocalDate subject, MonthDay min, MonthDay max) {
return subject.getMonth().compareTo(min.getMonth()) >= 0 &&
subject.getMonth().compareTo(max.getMonth()) <= 0 &&
// if the day is less than the minimum day, the minimum month must not be equal
(subject.getDayOfMonth() >= min.getDayOfMonth() || !subject.getMonth().equals(min.getMonth())) &&
// if the day is greater than the maximum day, the maximum month must not be equal
(subject.getDayOfMonth() <= max.getDayOfMonth() || !subject.getMonth().equals(max.getMonth()));
} }
} }

View File

@ -9,8 +9,6 @@ module ch.zhaw.gartenverwaltung {
opens ch.zhaw.gartenverwaltung to javafx.fxml; opens ch.zhaw.gartenverwaltung to javafx.fxml;
opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind;
exports ch.zhaw.gartenverwaltung; exports ch.zhaw.gartenverwaltung;
exports ch.zhaw.gartenverwaltung.io;
exports ch.zhaw.gartenverwaltung.types; exports ch.zhaw.gartenverwaltung.types;
exports ch.zhaw.gartenverwaltung.models;
exports ch.zhaw.gartenverwaltung.json; exports ch.zhaw.gartenverwaltung.json;
} }

View File

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

View File

@ -7,11 +7,11 @@
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyGardenController" fx:id="myGardenRoot"> <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyPlantsController">
<children> <children>
<VBox layoutY="49.0" prefHeight="655.0" prefWidth="1175.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <VBox layoutY="49.0" prefHeight="655.0" prefWidth="1175.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children> <children>
<Label text="My Garden"> <Label text="MyPlants">
<font> <font>
<Font name="System Bold" size="28.0" /> <Font name="System Bold" size="28.0" />
</font> </font>

View File

@ -15,8 +15,7 @@
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0" <AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.zhaw.gartenverwaltung.PlantsController" fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
fx:id="plantsRoot">
<children> <children>
<SplitPane dividerPositions="0.7377363661277062" layoutX="539.0" layoutY="266.0" prefHeight="853.0" prefWidth="1219.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <SplitPane dividerPositions="0.7377363661277062" layoutX="539.0" layoutY="266.0" prefHeight="853.0" prefWidth="1219.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items> <items>

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.DatePicker?> <?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?> <?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?> <?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.AnchorPane?>
@ -14,19 +16,20 @@
<children> <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"> <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> <children>
<Label fx:id="popup_label" text="Label" />
<HBox alignment="CENTER" prefHeight="293.0" prefWidth="631.0"> <HBox alignment="CENTER" prefHeight="293.0" prefWidth="631.0">
<children> <children>
<VBox alignment="CENTER_LEFT" prefHeight="88.0" prefWidth="155.0"> <VBox alignment="CENTER_LEFT" prefHeight="88.0" prefWidth="155.0">
<children> <children>
<RadioButton fx:id="sow_radio" mnemonicParsing="false" selected="true" text="Sow" toggleGroup="$phase_group"> <RadioButton fx:id="sow_radio" mnemonicParsing="false" selected="true" text="Sow">
<VBox.margin> <VBox.margin>
<Insets bottom="10.0" /> <Insets bottom="10.0" />
</VBox.margin> </VBox.margin>
<toggleGroup> <toggleGroup>
<ToggleGroup fx:id="phase_group" /> <ToggleGroup fx:id="group" />
</toggleGroup> </toggleGroup>
</RadioButton> </RadioButton>
<RadioButton fx:id="harvest_radio" mnemonicParsing="false" text="Harvest" toggleGroup="$phase_group" /> <RadioButton fx:id="harvest_radio" mnemonicParsing="false" text="Harvest" toggleGroup="$group" />
</children> </children>
<HBox.margin> <HBox.margin>
<Insets top="10.0" /> <Insets top="10.0" />
@ -35,6 +38,16 @@
<DatePicker fx:id="datepicker" /> <DatePicker fx:id="datepicker" />
</children> </children>
</HBox> </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> </children>
<padding> <padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />

View File

@ -1,32 +1,28 @@
package ch.zhaw.gartenverwaltung.models; package ch.zhaw.gartenverwaltung.gardenplan;
import ch.zhaw.gartenverwaltung.io.*; import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.*; import ch.zhaw.gartenverwaltung.types.*;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.MonthDay; import java.time.MonthDay;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
public class GardenPlanModelTest { public class GardenPlanModelTest {
private final URL dbDataSource = JsonCropListTest.class.getResource("test-user-crops.json"); GardenPlan gardenPlan;
private final URL testFile = JsonCropListTest.class.getResource("template-user-crops.json"); List<Crop> cropList;
CropList cropList;
List<Crop> exampleCrops;
Crop exampleCropOnion; Crop exampleCropOnion;
Crop exampleCropCarrot; Crop exampleCropCarrot;
Crop exampleCrop1; Crop exampleCrop1;
@ -34,10 +30,10 @@ public class GardenPlanModelTest {
Crop exampleCrop3; Crop exampleCrop3;
Plant examplePlantOnion; Plant examplePlantOnion;
Plant examplePlantCarrot; Plant examplePlantCarrot;
Garden model; Gardenplanmodel model;
@BeforeEach @BeforeEach
void setUp() throws IOException, URISyntaxException { void setUp() throws IOException {
examplePlantOnion = new Plant( examplePlantOnion = new Plant(
@ -76,48 +72,41 @@ public class GardenPlanModelTest {
exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1)); exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1));
exampleCrop2.withId(1); exampleCrop2.withId(1);
exampleCrop2.withArea(0.5); exampleCrop2.withArea(0.5);
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,1)); exampleCrop3 = new Crop(0,LocalDate.of(2023,3,01));
exampleCrop3.withId(2); exampleCrop3.withId(2);
exampleCrop3.withArea(1.0); exampleCrop3.withArea(1.0);
exampleCrops = new ArrayList<>(); cropList = new ArrayList<>();
exampleCrops.add(exampleCrop1); cropList.add(exampleCrop1);
exampleCrops.add(exampleCrop2); cropList.add(exampleCrop2);
exampleCrops.add(exampleCrop3); cropList.add(exampleCrop3);
cropList = mockCropList(exampleCrops); gardenPlan = mockGardenPlan(cropList);
// Reset Crop "database" before test TaskListModel taskListModel = new TaskListModel(new JsonTaskDatabase(), new JsonPlantDatabase());
assertNotNull(testFile); model = new Gardenplanmodel(taskListModel);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
CropList testDatabase = new JsonCropList(dbDataSource);
GardenSchedule gardenSchedule = mock(GardenSchedule.class);
model = new Garden(gardenSchedule, testDatabase);
} }
CropList mockCropList(List<Crop> cropList) throws IOException { GardenPlan mockGardenPlan(List<Crop> cropList) throws IOException {
CropList croplist = mock(CropList.class); GardenPlan gardenPlan = mock(GardenPlan.class);
when(croplist.getCrops()).thenReturn(cropList); when(gardenPlan.getCrops()).thenReturn(cropList);
when(croplist.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot)); when(gardenPlan.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
when(croplist.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion)); when(gardenPlan.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
return croplist; return gardenPlan;
} }
@Test @Test
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException { void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1)); model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
Optional<Crop> exampleCrop = model.getCrop(2L); exampleCropOnion = model.getCrop(2L).get();
assertTrue(exampleCrop.isPresent()); assertEquals(model.getCrops().get(2),exampleCropOnion);
assertEquals(model.getCrops().get(2), exampleCrop.get());
} }
@Test @Test
void removeCrop() throws IOException { void removeCrop() throws IOException {
exampleCrop1.withId(2); exampleCrop1.withId(2);
exampleCrop1.withArea(1.5); exampleCrop1.withArea(1.500000);
model.removeCrop(exampleCrop1); model.removeCrop(exampleCrop1);
assertEquals(2,model.getCrops().size()); assertEquals(2,model.getCrops().size());
} }

View File

@ -22,21 +22,21 @@ import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
public class JsonCropListTest { public class JsonGardenPlanTest {
private CropList testDatabase; private GardenPlan testDatabase;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/** /**
* Files to isolate the test-units * Files to isolate the test-units
*/ */
private final URL dbDataSource = this.getClass().getResource("test-user-crops.json"); private final URL dbDataSource = this.getClass().getResource("user-crops.json");
private final URL testFile = this.getClass().getResource("template-user-crops.json"); private final URL testFile = this.getClass().getResource("test-user-crops.json");
@BeforeEach @BeforeEach
void connectToDb() throws URISyntaxException, IOException { void connectToDb() throws URISyntaxException, IOException {
assertNotNull(testFile); assertNotNull(testFile);
assertNotNull(dbDataSource); assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING); Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
testDatabase = new JsonCropList(dbDataSource); testDatabase = new JsonGardenPlan(dbDataSource);
} }

View File

@ -8,22 +8,17 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertNotNull; public class JsonPlantDatabaseTest {
PlantDatabase testDatabase;
public class JsonPlantListTest {
private final URL testFile = this.getClass().getResource("test-plantdb.json");
PlantList testDatabase;
@BeforeEach @BeforeEach
void connectToDb() { void connectToDb() {
assertNotNull(testFile); testDatabase = new JsonPlantDatabase();
testDatabase = new JsonPlantList(testFile);
} }

View File

@ -9,36 +9,40 @@ import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
public class JsonTaskListTest { public class JsonTaskDatabaseTest {
TaskList testDatabase; TaskDatabase testDatabase;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private final URL dbDataSource = this.getClass().getResource("test-taskdb.json"); private final URL dbDataSource = this.getClass().getResource("taskdb.json");
private final URL testFile = this.getClass().getResource("template-taskdb.json"); private final URL testFile = this.getClass().getResource("test-taskdb.json");
@BeforeEach @BeforeEach
void connectToDb() throws URISyntaxException, IOException { void connectToDb() throws URISyntaxException, IOException {
assertNotNull(testFile); assertNotNull(testFile);
assertNotNull(dbDataSource); assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING); Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
testDatabase = new JsonTaskList(dbDataSource); testDatabase = new JsonTaskDatabase();
} }
@Test @Test
@DisplayName("Check if results are retrieved completely") @DisplayName("Check if results are retrieved completely")
void getTasks() { void getTasks() {
List<Task> taskList; List<Task> taskList = null;
try { try {
taskList = testDatabase.getTaskList(LocalDate.parse("2022-04-30", formatter), taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("2022-05-31", formatter)); LocalDate.parse("31.05.2022", formatter));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -47,16 +51,17 @@ public class JsonTaskListTest {
} }
@Disabled("disabled until adding works.")
@Test @Test
@DisplayName("Add task.") @DisplayName("Add task.")
void addTask() { void addTask() {
Task task = new Task("Testtask", "This is a test Task.", LocalDate.parse("2022-05-01", formatter), 1); Task task = new Task("Testtask", "This is a test Task.", LocalDate.parse("01.05.2022", formatter), 1);
try { try {
testDatabase.saveTask(task); testDatabase.saveTask(task);
List<Task> taskList; List<Task> taskList = null;
try { try {
taskList = testDatabase.getTaskList(LocalDate.parse("2022-04-30", formatter), taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("2022-05-31", formatter)); LocalDate.parse("31.05.2022", formatter));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -71,13 +76,13 @@ public class JsonTaskListTest {
@Test @Test
@DisplayName("Remove task.") @DisplayName("Remove task.")
void removeTask() { void removeTask() {
Task task = new Task("Dummy", "Dummy", LocalDate.parse("2022-05-31", formatter), 1).withId(2); Task task = new Task("Dummy", "Dummy", LocalDate.parse("31.05.2022", formatter), 1).withId(2);
try { try {
testDatabase.removeTask(task); testDatabase.removeTask(task);
List<Task> taskList; List<Task> taskList = null;
taskList = testDatabase.getTaskList(LocalDate.parse("2022-04-30", formatter), taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("2022-05-31", formatter)); LocalDate.parse("31.05.2022", formatter));
Assertions.assertEquals(2, taskList.size()); Assertions.assertEquals(2, taskList.size());
} catch (IOException e) { } catch (IOException e) {
@ -89,7 +94,7 @@ public class JsonTaskListTest {
@Test @Test
void getTaskForCrop() { void getTaskForCrop() {
List<Task> taskList; List<Task> taskList = null;
try { try {
taskList = testDatabase.getTaskForCrop(0); taskList = testDatabase.getTaskForCrop(0);
} catch (IOException e) { } catch (IOException e) {
@ -100,9 +105,10 @@ public class JsonTaskListTest {
} }
@Disabled("Disabled until removing works")
@Test @Test
void removeTasksForCrop() { void removeTasksForCrop() {
List<Task> taskList; List<Task> taskList = null;
try { try {
testDatabase.removeTasksForCrop(0); testDatabase.removeTasksForCrop(0);
taskList = testDatabase.getTaskForCrop(0); taskList = testDatabase.getTaskForCrop(0);

View File

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

View File

@ -1,7 +1,6 @@
package ch.zhaw.gartenverwaltung.taskList; package ch.zhaw.gartenverwaltung.taskList;
import ch.zhaw.gartenverwaltung.io.*; import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task; import ch.zhaw.gartenverwaltung.types.Task;
@ -15,47 +14,47 @@ import java.util.*;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
class GardenScheduleTest { class TaskListModelTest {
TaskList taskList; TaskDatabase taskDatabase;
PlantList plantList; PlantDatabase plantDatabase;
List<Task> exampleTaskList; List<Task> exampleTaskList;
Map<Long, Plant> examplePlantMap; Map<Long, Plant> examplePlantMap;
GardenSchedule model; TaskListModel model;
@BeforeEach @BeforeEach
void setUp() throws IOException { void setUp() throws IOException {
createExampleTaskList(); createExampleTaskList();
taskList = mockTaskDatabase(exampleTaskList); taskDatabase = mockTaskDatabase(exampleTaskList);
plantList = mockPlantDatabase(examplePlantMap); plantDatabase = mockPlantDatabase(examplePlantMap);
model = new GardenSchedule(taskList, plantList); model = new TaskListModel(taskDatabase, plantDatabase);
} }
private TaskList mockTaskDatabase(List<Task> exampleTaskList) throws IOException { private TaskDatabase mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
TaskList taskList = mock(JsonTaskList.class); TaskDatabase taskDatabase = mock(JsonTaskDatabase.class);
when(taskList.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList); when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
when(taskList.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4))); when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
List<Task> pastTasks = new ArrayList<>(); List<Task> pastTasks = new ArrayList<>();
pastTasks.add(exampleTaskList.get(0)); pastTasks.add(exampleTaskList.get(0));
pastTasks.add(exampleTaskList.get(2)); pastTasks.add(exampleTaskList.get(2));
pastTasks.add(exampleTaskList.get(4)); pastTasks.add(exampleTaskList.get(4));
when(taskList.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks); when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);
when(taskList.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2))); when(taskDatabase.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(taskDatabase.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(taskDatabase.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(taskDatabase.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(taskDatabase.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(taskDatabase.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()); when(taskDatabase.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());
return taskList; return taskDatabase;
} }
private PlantList mockPlantDatabase(Map<Long, Plant> examplePlantMap) { private PlantDatabase mockPlantDatabase(Map<Long, Plant> examplePlantMap) {
return new PlantList() { return new PlantDatabase() {
@Override @Override
public List<Plant> getPlantList(HardinessZone zone) { public List<Plant> getPlantList(HardinessZone zone) {
return null; return null;
@ -87,14 +86,14 @@ class GardenScheduleTest {
void addTask() throws IOException { void addTask() throws IOException {
Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L); Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
model.addTask(taskToAdd); model.addTask(taskToAdd);
verify(taskList, times(1)).saveTask(taskToAdd); verify(taskDatabase, times(1)).saveTask(taskToAdd);
} }
@Test @Test
void removeTask() throws IOException { void removeTask() throws IOException {
Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L); Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
model.removeTask(taskToRemove); model.removeTask(taskToRemove);
verify(taskList, times(1)).removeTask(taskToRemove); verify(taskDatabase, times(1)).removeTask(taskToRemove);
} }
@Test @Test
@ -154,6 +153,6 @@ class GardenScheduleTest {
@Test @Test
void removeTasksForCrop() throws IOException { void removeTasksForCrop() throws IOException {
model.removeTasksForCrop(1L); model.removeTasksForCrop(1L);
verify(taskList, times(1)).removeTasksForCrop(1L); verify(taskDatabase, times(1)).removeTasksForCrop(1L);
} }
} }

View File

@ -31,7 +31,7 @@
"name": "Germinate", "name": "Germinate",
"relativeStartDate": -14, "relativeStartDate": -14,
"relativeEndDate": null, "relativeEndDate": null,
"description": "Take an egg carton and fill it with soil. Put the seedling deep enough 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 enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
"interval": null, "interval": null,
"isOptional": false "isOptional": false
} }
@ -53,7 +53,7 @@
"name": "hilling", "name": "hilling",
"relativeStartDate": 0, "relativeStartDate": 0,
"relativeEndDate": null, "relativeEndDate": null,
"description": "When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.", "description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"",
"interval": 21, "interval": 21,
"isOptional": false "isOptional": false
} }
@ -94,7 +94,6 @@
"endDate": "03-10", "endDate": "03-10",
"zone": "ZONE_8A", "zone": "ZONE_8A",
"type": "SOW", "type": "SOW",
"group": 0,
"wateringCycle": { "wateringCycle": {
"litersPerSqM": 15, "litersPerSqM": 15,
"interval": 3, "interval": 3,
@ -116,7 +115,6 @@
"endDate": "05-10", "endDate": "05-10",
"zone": "ZONE_8A", "zone": "ZONE_8A",
"type": "PLANT", "type": "PLANT",
"group": 0,
"wateringCycle": { "wateringCycle": {
"litersPerSqM": 25, "litersPerSqM": 25,
"interval": 3, "interval": 3,
@ -140,7 +138,6 @@
"endDate": "05-20", "endDate": "05-20",
"zone": "ZONE_8A", "zone": "ZONE_8A",
"type": "HARVEST", "type": "HARVEST",
"group": 0,
"wateringCycle": { "wateringCycle": {
"litersPerSqM": 0, "litersPerSqM": 0,
"interval": null, "interval": null,

View File

@ -0,0 +1,257 @@
[
{
"id": 0,
"name": "Potato",
"description": "The potato is a tuber, round or oval, with small white roots called 'eyes', that are growth buds. The size varies depending on the variety; the colour of the skin can be white, yellow or even purple.",
"light": 6,
"spacing": "35",
"soil": "sandy",
"image": "potato.jpg",
"pests": [
{
"name": "Rot",
"description": "Rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
"measures": "Less water."
}
],
"lifecycle": [
{
"startDate": "03-10",
"endDate": "04-10",
"type": "SOW",
"zone": "ZONE_8A",
"group": 0,
"wateringCycle": {
"litersPerSqM": 0,
"interval": null,
"notes": []
},
"taskTemplates": [
{
"name": "Germinate",
"relativeStartDate": -14,
"relativeEndDate": null,
"description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
"interval": null,
"isOptional": false
}
]
},
{
"startDate": "04-10",
"endDate": "07-10",
"type": "PLANT",
"zone": "ZONE_8A",
"group": 0,
"wateringCycle": {
"litersPerSqM": 25,
"interval": 7,
"notes": []
},
"taskTemplates": [
{
"name": "hilling",
"relativeStartDate": 0,
"relativeEndDate": null,
"description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"",
"interval": 21,
"isOptional": false
}
]
},
{
"startDate": "06-10",
"endDate": "08-10",
"type": "HARVEST",
"zone": "ZONE_8A",
"group": 0,
"wateringCycle": {
"litersPerSqM": 0,
"interval": null,
"notes": []
},
"taskTemplates": [
{
"name": "Harvest",
"relativeStartDate": 0,
"relativeEndDate": null,
"description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.",
"interval": null,
"isOptional": false
}
]
}
]
},
{
"id": 1,
"name": "Early Carrot",
"description": "Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.",
"image": "carrot.jpg",
"lifecycle": [
{
"startDate": "02-20",
"endDate": "03-10",
"zone": "ZONE_8A",
"type": "SOW",
"wateringCycle": {
"litersPerSqM": 15,
"interval": 3,
"notes": []
},
"taskTemplates": [
{
"name": "hilling",
"relativeStartDate": 0,
"relativeEndDate": 0,
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
"interval": null,
"isOptional": false
}
]
},
{
"startDate": "03-10",
"endDate": "05-10",
"zone": "ZONE_8A",
"type": "PLANT",
"wateringCycle": {
"litersPerSqM": 25,
"interval": 3,
"notes": [
"Be careful not to pour water over the leaves, as this will lead to sunburn."
]
},
"taskTemplates": [
{
"name": "hilling",
"relativeStartDate": 0,
"relativeEndDate": null,
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
"interval": 15,
"isOptional": true
}
]
},
{
"startDate": "05-10",
"endDate": "05-20",
"zone": "ZONE_8A",
"type": "HARVEST",
"wateringCycle": {
"litersPerSqM": 0,
"interval": null,
"notes": []
},
"taskTemplates": [
{
"name": "Harvesting",
"relativeStartDate": 0,
"relativeEndDate": 14,
"description": "When the leaves turn to a yellowish brown. Do not harvest earlier. The plant will show when it's ready.",
"interval": null,
"isOptional": false
}
]
}
],
"soil": "sandy to loamy, loose soil, free of stones",
"spacing": "5,35,2.5",
"pests": [
{
"name": "Rot",
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
"measures": "less water"
}
]
},
{
"id": 2,
"name": "Summertime Onion",
"description": "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
"image": "onion.jpg",
"lifecycle": [
{
"startDate": "03-15",
"endDate": "04-10",
"type": "SOW",
"zone": "ZONE_8A",
"group": 0,
"wateringCycle": {
"litersPerSqM": 15,
"interval": 4,
"notes": [
]
},
"taskTemplates": [
{
"name": "hilling",
"relativeStartDate": 0,
"relativeEndDate": 0,
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
"interval": null,
"isOptional": false
}
]
},
{
"startDate": "04-10",
"endDate": "07-10",
"type": "PLANT",
"zone": "ZONE_8A",
"group": 0,
"wateringCycle": {
"litersPerSqM": 25,
"interval": 3,
"notes": [
""
]
},
"taskTemplates": [
{
"name": "hilling",
"relativeStartDate": 0,
"relativeEndDate": null,
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
"interval": 15,
"isOptional": true
}
]
},
{
"startDate": "07-10",
"endDate": "09-20",
"type": "HARVEST",
"zone": "ZONE_8A",
"group": 0,
"wateringCycle": {
"litersPerSqM": 0,
"interval": null,
"notes": [
]
},
"taskTemplates": [
{
"name": "Harvesting",
"relativeStartDate": 0,
"relativeEndDate": 14,
"description": "When ready for harvest, the leaves on your onion plants will start to flop over. This happens at the \"neck\" of the onion and it signals that the plant has stopped growing and is ready for storage. Onions should be harvested soon thereafter",
"interval": null,
"isOptional": false
}
]
}
],
"soil": "sandy to loamy, loose soil, free of stones",
"spacing": "15,30,2",
"pests": [
{
"name": "Rot",
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
"measures": "less water"
}
]
}
]

View File

@ -1,20 +0,0 @@
[
{
"cropId": 0,
"plantId": 1,
"startDate": "2023-02-25",
"area": 0.5
},
{
"cropId": 1,
"plantId": 1,
"startDate": "2023-03-01",
"area": 0.5
},
{
"cropId": 2,
"plantId": 0,
"startDate": "2023-03-25",
"area": 1.5
}
]

View File

@ -1,2 +1,56 @@
[ [
{
"id" : 1,
"name" : "sow plant",
"description": "Plant the seeds, crops in de bed.",
"startDate" : "2022-05-01",
"endDate" : "2022-05-01",
"interval" : 0,
"cropId" : 0
},
{
"id" : 2,
"name" : "water plant",
"description": "water the plant, so that the soil is wet around the plant.",
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 2,
"cropId" : 0
},
{
"id" : 3,
"name" : "fertilize plant",
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
"startDate" : "2022-06-01",
"endDate" : "2022-08-01",
"interval" : 28,
"cropId" : 0
},
{
"id" : 4,
"name" : "covering plant",
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
"startDate" : "2022-07-01",
"endDate" : "2022-07-01",
"interval" : 0,
"cropId" : 0
},
{
"id" : 5,
"name" : "look after plant",
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 5,
"cropId" : 0
},
{
"id" : 6,
"name" : "harvest plant",
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
"startDate" : "2022-09-01",
"endDate" : "2022-09-01",
"interval" : 0,
"cropId" : 0
}
] ]

View File

@ -1,2 +1,20 @@
[ [
{
"cropId": 0,
"plantId": 1,
"startDate": "2023-02-25",
"area": 0.5
},
{
"cropId": 1,
"plantId": 1,
"startDate": "2023-03-01",
"area": 0.5
},
{
"cropId": 2,
"plantId": 0,
"startDate": "2023-03-25",
"area": 1.5
}
] ]

View File

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