Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f049f86ab | ||
|
|
bcb36b89c7 | ||
|
|
f7105f041c | ||
|
|
b0369e3174 | ||
|
|
8c20dcaadb | ||
|
|
95b0f7e13d | ||
|
|
2312149256 | ||
|
|
ffc7f5174e | ||
|
|
8d3fbc06ad | ||
|
|
dcb97f1c55 | ||
|
|
ee67c83b22 | ||
|
|
2963872237 | ||
|
|
7920bdff28 | ||
|
|
e96280cd0c | ||
|
|
bfe3fcfb79 | ||
|
|
b70a758099 | ||
|
|
3a69119eb7 | ||
|
|
2f69c48800 | ||
|
|
86a9eeaf2e | ||
|
|
9ba252b828 | ||
|
|
90d2de65de | ||
|
|
05e7bcc2e8 | ||
|
|
09e582b8a2 | ||
|
|
2b7cec7e6a |
@@ -1,5 +1,6 @@
|
|||||||
package ch.zhaw.gartenverwaltung;
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
|
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
|
||||||
import ch.zhaw.gartenverwaltung.io.PlantList;
|
import ch.zhaw.gartenverwaltung.io.PlantList;
|
||||||
import ch.zhaw.gartenverwaltung.models.Garden;
|
import ch.zhaw.gartenverwaltung.models.Garden;
|
||||||
@@ -10,13 +11,18 @@ 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.beans.property.ListProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -34,7 +40,12 @@ public class CropDetailController {
|
|||||||
@Inject
|
@Inject
|
||||||
private Garden garden;
|
private Garden garden;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AppLoader appLoader;
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
|
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
|
||||||
|
private final ListProperty<Task> taskListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
private final ListProperty<Pest> pestListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ImageView imageView;
|
private ImageView imageView;
|
||||||
@@ -51,9 +62,6 @@ public class CropDetailController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Label description_label;
|
private Label description_label;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private VBox growthPhases_vbox;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label location_label;
|
private Label location_label;
|
||||||
|
|
||||||
@@ -63,9 +71,6 @@ public class CropDetailController {
|
|||||||
@FXML
|
@FXML
|
||||||
private Button location_button;
|
private Button location_button;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private VBox pests_vbox;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label soil_label;
|
private Label soil_label;
|
||||||
|
|
||||||
@@ -73,28 +78,53 @@ public class CropDetailController {
|
|||||||
private Label spacing_label;
|
private Label spacing_label;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void editTaskList() {
|
private Button addTask_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<Task> taskList_listView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<Pest> pests_listView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void addTask() throws IOException {
|
||||||
|
createTaskDialog(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close Window
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void goBack() {
|
void goBack() {
|
||||||
Stage stage = (Stage) imageView.getScene().getWindow();
|
Stage stage = (Stage) imageView.getScene().getWindow();
|
||||||
stage.close();
|
stage.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open dialog to set area
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void setArea() {
|
void setArea() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open dialog to set location
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void setLocation() {
|
void setLocation() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
/**
|
||||||
|
* set labels and image from selected {@link Crop}
|
||||||
|
* set icons for buttons
|
||||||
|
* @param crop {@link Crop} which will be displayed
|
||||||
|
* @throws PlantNotFoundException exception
|
||||||
|
*/
|
||||||
|
public void setPlantFromCrop(Crop crop) throws PlantNotFoundException {
|
||||||
this.crop = crop;
|
this.crop = crop;
|
||||||
|
try {
|
||||||
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
|
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
|
||||||
.orElseThrow(PlantNotFoundException::new);
|
.orElseThrow(PlantNotFoundException::new);
|
||||||
|
|
||||||
@@ -108,19 +138,65 @@ public class CropDetailController {
|
|||||||
}
|
}
|
||||||
area_label.setText("");
|
area_label.setText("");
|
||||||
location_label.setText("");
|
location_label.setText("");
|
||||||
createTaskLists(crop);
|
|
||||||
createPestList(plant);
|
setTaskListProperty(crop);
|
||||||
|
taskList_listView.itemsProperty().bind(taskListProperty);
|
||||||
|
|
||||||
|
pestListProperty.addAll(plant.pests());
|
||||||
|
pests_listView.itemsProperty().bind(pestListProperty);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (HardinessZoneNotSetException | IOException e) {
|
||||||
|
throw new PlantNotFoundException();
|
||||||
|
}
|
||||||
|
setIconToButton(addTask_button, "addIcon.png");
|
||||||
|
setIconToButton(area_button, "areaIcon.png");
|
||||||
|
setIconToButton(location_button, "locationIcon.png");
|
||||||
|
setCellFactoryPests();
|
||||||
|
setCellFactoryTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTaskLists(Crop crop) {
|
private void setCellFactoryTasks() {
|
||||||
|
taskList_listView.setCellFactory(param -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Task task, boolean empty) {
|
||||||
|
super.updateItem(task, empty);
|
||||||
|
|
||||||
|
if (empty || task == null) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
setText("");
|
||||||
|
setGraphic(createTaskHBox(task));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCellFactoryPests() {
|
||||||
|
pests_listView.setCellFactory(param -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Pest pest, boolean empty) {
|
||||||
|
super.updateItem(pest, empty);
|
||||||
|
|
||||||
|
if (empty || pest == null) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
setText("");
|
||||||
|
setGraphic(createPestHBox(pest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTaskListProperty(Crop crop) {
|
||||||
crop.getCropId().ifPresent(id -> {
|
crop.getCropId().ifPresent(id -> {
|
||||||
List<Task> taskList;
|
List<Task> taskList;
|
||||||
try {
|
try {
|
||||||
taskList = gardenSchedule.getTaskListForCrop(id);
|
taskList = gardenSchedule.getTaskListForCrop(id);
|
||||||
for (Task task : taskList) {
|
taskListProperty.clear();
|
||||||
Label label = new Label(task.getDescription());
|
taskListProperty.addAll(taskList);
|
||||||
growthPhases_vbox.getChildren().add(label);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO: Alert
|
// TODO: Alert
|
||||||
LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
|
LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
|
||||||
@@ -128,10 +204,27 @@ public class CropDetailController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPestList(Plant plant) {
|
private HBox createTaskHBox(Task task) {
|
||||||
List<Pest> pests = plant.pests();
|
HBox hBox = new HBox();
|
||||||
for (Pest pest : pests) {
|
Label taskName = new Label(task.getName()+": ");
|
||||||
Label label = new Label(pest.name() + ":");
|
taskName.setStyle("-fx-font-weight: bold");
|
||||||
|
Label taskDescription = new Label(task.getDescription());
|
||||||
|
taskDescription.setWrapText(true);
|
||||||
|
taskDescription.setMaxWidth(2000);
|
||||||
|
HBox.setHgrow(taskDescription, Priority.ALWAYS);
|
||||||
|
|
||||||
|
Button edit = new Button();
|
||||||
|
Button delete = new Button();
|
||||||
|
setIconToButton(edit, "editIcon.png");
|
||||||
|
setIconToButton(delete, "deleteIcon.png");
|
||||||
|
edit.setOnAction(getEditTaskEvent(task));
|
||||||
|
|
||||||
|
hBox.getChildren().addAll(taskName, taskDescription, edit, delete);
|
||||||
|
return hBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createPestHBox(Pest pest) {
|
||||||
|
Label label = new Label(pest.name() + ": ");
|
||||||
label.setStyle("-fx-font-weight: bold");
|
label.setStyle("-fx-font-weight: bold");
|
||||||
HBox hBox = new HBox();
|
HBox hBox = new HBox();
|
||||||
hBox.fillHeightProperty();
|
hBox.fillHeightProperty();
|
||||||
@@ -139,10 +232,72 @@ public class CropDetailController {
|
|||||||
label1.setAlignment(Pos.TOP_LEFT);
|
label1.setAlignment(Pos.TOP_LEFT);
|
||||||
label1.setWrapText(true);
|
label1.setWrapText(true);
|
||||||
label1.setMaxWidth(600);
|
label1.setMaxWidth(600);
|
||||||
label1.setMaxHeight(100);
|
|
||||||
Button button = new Button("Get Counter Measures");
|
Button button = new Button("Get Counter Measures");
|
||||||
hBox.getChildren().addAll(label1, button);
|
hBox.getChildren().addAll(label, label1, button);
|
||||||
pests_vbox.getChildren().addAll(label, hBox);
|
return hBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds icon to button
|
||||||
|
* @param button the button which get the icon
|
||||||
|
* @param iconFileName file name of icon
|
||||||
|
*/
|
||||||
|
private void setIconToButton(Button button, String iconFileName) {
|
||||||
|
Image img = new Image(String.valueOf(getClass().getResource("icons/" + iconFileName)));
|
||||||
|
ImageView imageView = new ImageView(img);
|
||||||
|
imageView.setFitHeight(20);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
button.setGraphic(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventHandler<ActionEvent> getEditTaskEvent(Task task) {
|
||||||
|
return (event) -> {
|
||||||
|
try {
|
||||||
|
createTaskDialog(false, task);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTaskDialog(boolean newTask, Task givenTask) throws IOException {
|
||||||
|
Dialog<Task> dialog = new Dialog<>();
|
||||||
|
dialog.setTitle("Set Task");
|
||||||
|
dialog.setHeaderText("Add/Edit Task:");
|
||||||
|
dialog.setResizable(false);
|
||||||
|
|
||||||
|
DialogPane dialogPane = dialog.getDialogPane();
|
||||||
|
|
||||||
|
ButtonType saveTask;
|
||||||
|
if(newTask) {
|
||||||
|
saveTask = new ButtonType("Add", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
} else {
|
||||||
|
saveTask = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
}
|
||||||
|
dialogPane.getButtonTypes().addAll(saveTask, ButtonType.CANCEL);
|
||||||
|
|
||||||
|
if (appLoader.loadPaneToDialog("TaskFormular.fxml", dialogPane) instanceof TaskFormularController controller) {
|
||||||
|
controller.setCorp(this.crop);
|
||||||
|
if (!newTask) {
|
||||||
|
controller.setTaskValue(givenTask);
|
||||||
|
}
|
||||||
|
dialog.setResultConverter(button -> button.equals(saveTask) ? controller.returnResult() : null);
|
||||||
|
|
||||||
|
dialog.showAndWait()
|
||||||
|
.ifPresent(task -> {
|
||||||
|
if (newTask) {
|
||||||
|
try {
|
||||||
|
gardenSchedule.addTask(task);
|
||||||
|
setTaskListProperty(this.crop);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//ToDo method to edit task
|
||||||
|
setTaskListProperty(this.crop);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package ch.zhaw.gartenverwaltung;
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.fxml.FXMLLoader;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -10,10 +9,11 @@ import java.io.IOException;
|
|||||||
public class HelloApplication extends Application {
|
public class HelloApplication extends Application {
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("MainFXML.fxml"));
|
AppLoader appLoader = new AppLoader();
|
||||||
Scene scene = new Scene(fxmlLoader.load());
|
|
||||||
|
appLoader.loadSceneToStage("MainFXML.fxml", stage);
|
||||||
|
|
||||||
stage.setTitle("Gartenverwaltung");
|
stage.setTitle("Gartenverwaltung");
|
||||||
stage.setScene(scene);
|
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,47 @@
|
|||||||
package ch.zhaw.gartenverwaltung;
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
public class HomeController
|
import javafx.fxml.FXML;
|
||||||
{
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class HomeController implements Initializable {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewDavid;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewElias;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewGian;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewPhilippe;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewRoman;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
setImages(imageViewDavid, "");
|
||||||
|
setImages(imageViewElias, "");
|
||||||
|
setImages(imageViewGian, "");
|
||||||
|
setImages(imageViewRoman, "");
|
||||||
|
setImages(imageViewPhilippe, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setImages(ImageView imageView, String photoName) {
|
||||||
|
Image img;
|
||||||
|
if (photoName.equals("")) {
|
||||||
|
img = new Image(String.valueOf(getClass().getResource("icons/userIcon.png")));
|
||||||
|
} else {
|
||||||
|
img = new Image(String.valueOf(getClass().getResource("icons/" + photoName)));
|
||||||
|
}
|
||||||
|
imageView.setImage(img);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,30 @@
|
|||||||
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.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.Initializable;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class MainFXMLController implements Initializable {
|
public class MainFXMLController {
|
||||||
private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName());
|
private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName());
|
||||||
private final AppLoader appLoader = new AppLoader(this);
|
|
||||||
|
@Inject
|
||||||
|
AppLoader appLoader;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button home_button;
|
private Button home_button;
|
||||||
|
|
||||||
@@ -29,33 +38,86 @@ public class MainFXMLController implements Initializable {
|
|||||||
private Button mySchedule_button;
|
private Button mySchedule_button;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button plants_button;
|
private Button settings_button;
|
||||||
|
|
||||||
public MainFXMLController() throws IOException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void goToHome() throws IOException {
|
private Button tutorial_button;
|
||||||
|
|
||||||
|
private final Stage tutorialModal = new Stage();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* go to home pane
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void goToHome() {
|
||||||
showPaneAsMainView("Home.fxml");
|
showPaneAsMainView("Home.fxml");
|
||||||
styleChangeButton(home_button);
|
styleChangeButton(home_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* go to my garden pane
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void goToMyPlants() throws IOException {
|
void goToMyPlants() {
|
||||||
showPaneAsMainView("MyGarden.fxml");
|
showPaneAsMainView("MyGarden.fxml");
|
||||||
styleChangeButton(myGarden_button);
|
styleChangeButton(myGarden_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* go to the schedule pane
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void goToMySchedule() throws IOException {
|
void goToMySchedule() {
|
||||||
showPaneAsMainView("MySchedule.fxml");
|
showPaneAsMainView("MySchedule.fxml");
|
||||||
styleChangeButton(mySchedule_button);
|
styleChangeButton(mySchedule_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open dialog of the settings
|
||||||
|
* @throws IOException exception
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void goToPlants() throws IOException {
|
public void openSettings() throws IOException {
|
||||||
showPaneAsMainView("Plants.fxml");
|
Dialog<ButtonType> dialog = new Dialog<>();
|
||||||
styleChangeButton(plants_button);
|
dialog.setTitle("Settings");
|
||||||
|
dialog.setHeaderText("Settings");
|
||||||
|
dialog.setResizable(false);
|
||||||
|
|
||||||
|
DialogPane dialogPane = dialog.getDialogPane();
|
||||||
|
|
||||||
|
ButtonType saveSettings = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
dialogPane.getButtonTypes().addAll(saveSettings, ButtonType.CANCEL);
|
||||||
|
|
||||||
|
if (appLoader.loadPaneToDialog("Settings.fxml", dialogPane) instanceof SettingsController controller) {
|
||||||
|
|
||||||
|
dialog.showAndWait()
|
||||||
|
.ifPresent(button -> {
|
||||||
|
if (button.equals(saveSettings)) {
|
||||||
|
controller.saveSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the tutorial window
|
||||||
|
*/
|
||||||
|
public void showTutorial() {
|
||||||
|
if (!tutorialModal.isShowing()) {
|
||||||
|
if (tutorialModal.getScene() == null) {
|
||||||
|
try {
|
||||||
|
appLoader.loadSceneToStage("Tutorial.fxml", tutorialModal);
|
||||||
|
tutorialModal.initModality(Modality.NONE);
|
||||||
|
tutorialModal.setResizable(false);
|
||||||
|
tutorialModal.sizeToScene();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.SEVERE, "Could not load Tutorial");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tutorialModal.show();
|
||||||
|
}
|
||||||
|
tutorialModal.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,16 +125,27 @@ public class MainFXMLController implements Initializable {
|
|||||||
* set HGrow and VGrow to parent AnchorPane.
|
* set HGrow and VGrow to parent AnchorPane.
|
||||||
* Sends MainController to other Controllers.
|
* Sends MainController to other Controllers.
|
||||||
* @param fxmlFile string of fxml file
|
* @param fxmlFile string of fxml file
|
||||||
* @throws IOException exception when file does not exist
|
|
||||||
*/
|
*/
|
||||||
public void showPaneAsMainView(String fxmlFile) throws IOException {
|
public void showPaneAsMainView(String fxmlFile) {
|
||||||
|
try {
|
||||||
Pane anchorPane = appLoader.loadPane(fxmlFile);
|
Pane anchorPane = appLoader.loadPane(fxmlFile);
|
||||||
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());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* preload all menu bar panes
|
||||||
|
* @throws IOException exception
|
||||||
|
*/
|
||||||
private void preloadPanes() throws IOException {
|
private void preloadPanes() throws IOException {
|
||||||
appLoader.loadAndCacheFxml("MyGarden.fxml");
|
appLoader.loadAndCacheFxml("MyGarden.fxml");
|
||||||
appLoader.loadAndCacheFxml("MySchedule.fxml");
|
appLoader.loadAndCacheFxml("MySchedule.fxml");
|
||||||
@@ -87,15 +160,36 @@ public class MainFXMLController implements Initializable {
|
|||||||
* loads the default FXML File
|
* loads the default FXML File
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@AfterInject
|
||||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
@SuppressWarnings("unused")
|
||||||
|
public void init() {
|
||||||
try {
|
try {
|
||||||
preloadPanes();
|
preloadPanes();
|
||||||
showPaneAsMainView("Home.fxml");
|
showPaneAsMainView("MyGarden.fxml");
|
||||||
styleChangeButton(home_button);
|
styleChangeButton(myGarden_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);
|
||||||
}
|
}
|
||||||
|
mainPane.getScene().getWindow().setOnCloseRequest(this::closeWindowHandler);
|
||||||
|
setIconToButton(home_button, "homeIcon.png");
|
||||||
|
setIconToButton(settings_button, "settingsIcon.png");
|
||||||
|
tutorial_button.visibleProperty().bind(Settings.getInstance().getShowTutorialProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeWindowHandler(WindowEvent windowEvent) {
|
||||||
|
tutorialModal.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds icon to given button
|
||||||
|
* @param button the button which get the icon
|
||||||
|
* @param iconFileName file name of icon
|
||||||
|
*/
|
||||||
|
private void setIconToButton(Button button, String iconFileName) {
|
||||||
|
Image img = new Image(String.valueOf(getClass().getResource("icons/" + iconFileName)));
|
||||||
|
ImageView imageView = new ImageView(img);
|
||||||
|
imageView.setFitHeight(20);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
button.setGraphic(imageView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ch.zhaw.gartenverwaltung;
|
|||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
|
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
|
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
|
||||||
|
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
|
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
|
||||||
import ch.zhaw.gartenverwaltung.io.PlantList;
|
import ch.zhaw.gartenverwaltung.io.PlantList;
|
||||||
import ch.zhaw.gartenverwaltung.models.Garden;
|
import ch.zhaw.gartenverwaltung.models.Garden;
|
||||||
@@ -12,14 +13,12 @@ import ch.zhaw.gartenverwaltung.types.Plant;
|
|||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.control.ButtonType;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
@@ -30,50 +29,74 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
public class MyGardenController {
|
public class MyGardenController {
|
||||||
private static final Logger LOG = Logger.getLogger(MyGardenController.class.getName());
|
private static final Logger LOG = Logger.getLogger(MyGardenController.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AppLoader appLoader;
|
AppLoader appLoader;
|
||||||
@Inject
|
@Inject
|
||||||
MainFXMLController mainController;
|
|
||||||
@Inject
|
|
||||||
private Garden garden;
|
private Garden garden;
|
||||||
@Inject
|
@Inject
|
||||||
private PlantList plantList;
|
private PlantList plantList;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private VBox myPlants_vbox;
|
public AnchorPane myGardenRoot;
|
||||||
|
@FXML
|
||||||
|
private Button addPlant_button;
|
||||||
|
@FXML
|
||||||
|
private ListView<Crop> myGarden_listView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize crop list
|
||||||
|
* add listener for crop list
|
||||||
|
* set icon for button
|
||||||
|
*/
|
||||||
@AfterInject
|
@AfterInject
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void init() {
|
public void init() {
|
||||||
garden.getPlantedCrops().addListener((observable, oldValue, newValue) -> {
|
System.out.println("once");
|
||||||
|
setIconToButton(addPlant_button, "addIcon.png");
|
||||||
|
myGarden_listView.itemsProperty().bind(garden.getPlantedCrops());
|
||||||
|
setCellFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* redirect to plant fxml file
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void addPlant() {
|
||||||
|
myGardenRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "Plants.fxml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set cell factory to load {@link HBox} as list view content
|
||||||
|
*/
|
||||||
|
private void setCellFactory() {
|
||||||
|
myGarden_listView.setCellFactory(param -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Crop crop, boolean empty) {
|
||||||
|
super.updateItem(crop, empty);
|
||||||
|
|
||||||
|
if (empty || crop == null) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
try {
|
try {
|
||||||
createPlantView(newValue);
|
setText("");
|
||||||
|
setGraphic(createHBoxForListView(crop));
|
||||||
} catch (HardinessZoneNotSetException | IOException e) {
|
} catch (HardinessZoneNotSetException | IOException e) {
|
||||||
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
|
LOG.log(Level.WARNING, "Could not get plant for Cell", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
|
||||||
createPlantView(garden.getPlantedCrops());
|
|
||||||
} catch (HardinessZoneNotSetException | IOException e) {
|
|
||||||
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
/**
|
||||||
void addPlant(ActionEvent event) throws IOException {
|
* Creates and returns HBox of the crop
|
||||||
mainController.showPaneAsMainView("Plants.fxml");
|
* @param crop {@link Crop} which is selected
|
||||||
}
|
* @return {@link HBox} of the {@link Crop}
|
||||||
|
* @throws HardinessZoneNotSetException exception
|
||||||
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
|
* @throws IOException exception
|
||||||
myPlants_vbox.getChildren().clear();
|
*/
|
||||||
for (Crop crop : crops) {
|
private HBox createHBoxForListView(Crop crop) throws HardinessZoneNotSetException, IOException {
|
||||||
HBox hBox = createPlantView(crop);
|
|
||||||
myPlants_vbox.getChildren().add(hBox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
|
|
||||||
//ToDo add better design
|
//ToDo add better design
|
||||||
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
|
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
|
||||||
HBox hBox = new HBox(10);
|
HBox hBox = new HBox(10);
|
||||||
@@ -89,14 +112,36 @@ public class MyGardenController {
|
|||||||
Label label = new Label(plant.name());
|
Label label = new Label(plant.name());
|
||||||
label.setMaxWidth(2000);
|
label.setMaxWidth(2000);
|
||||||
HBox.setHgrow(label, Priority.ALWAYS);
|
HBox.setHgrow(label, Priority.ALWAYS);
|
||||||
Button details = new Button("Details");
|
|
||||||
Button delete = new Button("delete");
|
Button details = new Button();
|
||||||
|
Button delete = new Button();
|
||||||
|
setIconToButton(details, "detailsIcon.png");
|
||||||
|
setIconToButton(delete, "deleteIcon.png");
|
||||||
details.setOnAction(getGoToCropDetailEvent(crop));
|
details.setOnAction(getGoToCropDetailEvent(crop));
|
||||||
delete.setOnAction(getDeleteCropEvent(crop));
|
delete.setOnAction(getDeleteCropEvent(crop));
|
||||||
|
|
||||||
hBox.getChildren().addAll(imageView, label, details, delete);
|
hBox.getChildren().addAll(imageView, label, details, delete);
|
||||||
return hBox;
|
return hBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds icon to button
|
||||||
|
* @param button the button which get the icon
|
||||||
|
* @param iconFileName file name of icon
|
||||||
|
*/
|
||||||
|
private void setIconToButton(Button button, String iconFileName) {
|
||||||
|
Image img = new Image(String.valueOf(getClass().getResource("icons/" + iconFileName)));
|
||||||
|
ImageView imageView = new ImageView(img);
|
||||||
|
imageView.setFitHeight(20);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
button.setGraphic(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open detail window of the selected {@link Crop}
|
||||||
|
* @param crop {@link Crop} which is selected
|
||||||
|
* @return {@link EventHandler} for button
|
||||||
|
*/
|
||||||
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
|
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
|
||||||
return (event) -> {
|
return (event) -> {
|
||||||
try {
|
try {
|
||||||
@@ -107,13 +152,18 @@ public class MyGardenController {
|
|||||||
stage.initModality(Modality.APPLICATION_MODAL);
|
stage.initModality(Modality.APPLICATION_MODAL);
|
||||||
stage.setResizable(true);
|
stage.setResizable(true);
|
||||||
stage.showAndWait();
|
stage.showAndWait();
|
||||||
} catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
|
} catch (IOException | PlantNotFoundException e) {
|
||||||
// TODO: show error alert
|
// TODO: show error alert
|
||||||
LOG.log(Level.SEVERE, "Could not load plant details.", e);
|
LOG.log(Level.SEVERE, "Could not load plant details.", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open alert for deleting the selected {@link Crop}
|
||||||
|
* @param crop {@link Crop} which is selected
|
||||||
|
* @return {@link EventHandler} for button
|
||||||
|
*/
|
||||||
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
|
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
|
||||||
return (event) -> {
|
return (event) -> {
|
||||||
try {
|
try {
|
||||||
@@ -124,11 +174,18 @@ public class MyGardenController {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert to confirm that the crop can be deleted.
|
||||||
|
* @param crop {@link Crop} which is selected
|
||||||
|
* @throws IOException exception
|
||||||
|
* @throws HardinessZoneNotSetException exception
|
||||||
|
*/
|
||||||
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
|
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
|
||||||
|
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
|
||||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
alert.setTitle("Delete Crop");
|
alert.setTitle("Delete " + plant.name());
|
||||||
alert.setHeaderText("Are you sure want to delete this Crop?");
|
alert.setHeaderText("Are you sure want to delete this Crop?");
|
||||||
alert.setContentText("placeholder");
|
alert.setContentText("Deleting this crop will remove all associated tasks from your schedule.");
|
||||||
|
|
||||||
alert.showAndWait()
|
alert.showAndWait()
|
||||||
.ifPresent(buttonType -> {
|
.ifPresent(buttonType -> {
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ import ch.zhaw.gartenverwaltung.models.GardenSchedule;
|
|||||||
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 javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
@@ -37,8 +34,6 @@ public class MyScheduleController {
|
|||||||
@Inject
|
@Inject
|
||||||
private PlantList plantList;
|
private PlantList plantList;
|
||||||
|
|
||||||
private final ListProperty<Crop> cropListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label day1_label;
|
private Label day1_label;
|
||||||
|
|
||||||
@@ -90,15 +85,8 @@ public class MyScheduleController {
|
|||||||
@AfterInject
|
@AfterInject
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void init() {
|
public void init() {
|
||||||
List<Crop> cropList;
|
|
||||||
try {
|
|
||||||
cropList = garden.getCrops();
|
|
||||||
cropListProperty.addAll(cropList);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
setCellFactoryListView();
|
setCellFactoryListView();
|
||||||
scheduledPlants_listview.itemsProperty().bind(cropListProperty);
|
scheduledPlants_listview.itemsProperty().bind(garden.getPlantedCrops());
|
||||||
lookForSelectedListEntries();
|
lookForSelectedListEntries();
|
||||||
setDayLabels();
|
setDayLabels();
|
||||||
information_label.setText("");
|
information_label.setText("");
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package ch.zhaw.gartenverwaltung;
|
|||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
|
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
|
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
|
||||||
|
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
|
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.models.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;
|
||||||
@@ -16,11 +19,11 @@ import javafx.geometry.Insets;
|
|||||||
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.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -32,13 +35,17 @@ public class PlantsController {
|
|||||||
private PlantListModel plantListModel;
|
private PlantListModel plantListModel;
|
||||||
@Inject
|
@Inject
|
||||||
private AppLoader appLoader;
|
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
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -65,14 +72,31 @@ public class PlantsController {
|
|||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void selectSowDate() throws IOException {
|
void selectSowDate() throws IOException {
|
||||||
Stage stage = new Stage();
|
Dialog<LocalDate> dateSelection = new Dialog<>();
|
||||||
if (appLoader.loadSceneToStage("SelectSowDay.fxml", stage) instanceof SelectSowDayController controller) {
|
dateSelection.setTitle("Select Date");
|
||||||
controller.setSelectedPlant(selectedPlant);
|
dateSelection.setHeaderText(String.format("Select Harvest/Sow Date for %s:", selectedPlant.name()));
|
||||||
}
|
dateSelection.setResizable(false);
|
||||||
|
|
||||||
stage.initModality(Modality.APPLICATION_MODAL);
|
DialogPane dialogPane = dateSelection.getDialogPane();
|
||||||
stage.setResizable(false);
|
|
||||||
stage.showAndWait();
|
ButtonType sowButton = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
dialogPane.getButtonTypes().addAll(sowButton, ButtonType.CANCEL);
|
||||||
|
|
||||||
|
if (appLoader.loadPaneToDialog("SelectSowDay.fxml", dialogPane) instanceof SelectSowDayController controller) {
|
||||||
|
controller.initSaveButton((Button) dialogPane.lookupButton(sowButton));
|
||||||
|
controller.setSelectedPlant(selectedPlant);
|
||||||
|
dateSelection.setResultConverter(button -> button.equals(sowButton) ? controller.retrieveResult() : null);
|
||||||
|
|
||||||
|
dateSelection.showAndWait()
|
||||||
|
.ifPresent(date -> {
|
||||||
|
try {
|
||||||
|
garden.plantAsCrop(selectedPlant, date);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
|
||||||
|
LOG.log(Level.SEVERE, "Couldn't save Crop", e);
|
||||||
|
}
|
||||||
|
plantsRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "MyGarden.fxml"));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,6 +150,7 @@ 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
|
||||||
@@ -138,6 +163,7 @@ 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
|
||||||
*/
|
*/
|
||||||
@@ -184,7 +210,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);
|
||||||
}
|
}
|
||||||
@@ -206,7 +232,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);
|
||||||
}
|
}
|
||||||
@@ -240,12 +266,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(getPlantDescription());
|
||||||
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")));
|
||||||
@@ -261,6 +287,20 @@ public class PlantsController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates {@link String} of the plant information.
|
||||||
|
* @return return {@link Plant} description
|
||||||
|
*/
|
||||||
|
private String getPlantDescription() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Name: ").append(selectedPlant.name())
|
||||||
|
.append("\nDescription:\n").append(selectedPlant.description())
|
||||||
|
.append("\nLight Level: ").append(selectedPlant.light())
|
||||||
|
.append("\nSoil: ").append(selectedPlant.soil())
|
||||||
|
.append("\nSpacing: ").append(selectedPlant.spacing());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clears the ListView of entries
|
* clears the ListView of entries
|
||||||
|
|||||||
@@ -1,88 +1,69 @@
|
|||||||
package ch.zhaw.gartenverwaltung;
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
|
|
||||||
import ch.zhaw.gartenverwaltung.models.Garden;
|
|
||||||
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
|
||||||
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
|
|
||||||
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.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 implements Initializable {
|
public class SelectSowDayController {
|
||||||
private Plant selectedPlant = null;
|
private Plant selectedPlant;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private Garden garden;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private DatePicker datepicker;
|
private DatePicker datepicker;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private Label popup_label;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private Button save_button;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private RadioButton harvest_radio;
|
private RadioButton harvest_radio;
|
||||||
|
@FXML
|
||||||
|
private RadioButton sow_radio;
|
||||||
|
@FXML
|
||||||
|
public ToggleGroup phase_group;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* close the date selector window
|
* if sow date radio button was selected return sow date
|
||||||
|
* if sow date was not selected get sow from harvest day and return sow date
|
||||||
|
* @return {@link LocalDate} of the sow date
|
||||||
*/
|
*/
|
||||||
@FXML
|
public LocalDate retrieveResult() {
|
||||||
void cancel() {
|
|
||||||
closeWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get sow date from datePicker or calculate sow date from harvest date
|
|
||||||
* save selected plant and sow date
|
|
||||||
*/
|
|
||||||
@FXML
|
|
||||||
void save() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
|
||||||
LocalDate sowDate = datepicker.getValue();
|
LocalDate sowDate = datepicker.getValue();
|
||||||
if (harvest_radio.isSelected()) {
|
if (harvest_radio.isSelected()) {
|
||||||
//ToDo method to get current lifecycle group in plant
|
sowDate = selectedPlant.sowDateFromHarvestDate(sowDate);
|
||||||
sowDate = selectedPlant.sowDateFromHarvestDate(datepicker.getValue(), 0);
|
|
||||||
}
|
}
|
||||||
garden.plantAsCrop(selectedPlant, sowDate);
|
return sowDate;
|
||||||
closeWindow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* save the plant which will be planted and update label
|
* Set the {@link Plant} for which a date should be selected.
|
||||||
|
*
|
||||||
* @param plant Plant
|
* @param plant Plant
|
||||||
*/
|
*/
|
||||||
public void setSelectedPlant(Plant plant) {
|
public void setSelectedPlant(Plant plant) {
|
||||||
selectedPlant = plant;
|
selectedPlant = plant;
|
||||||
popup_label.setText(String.format("Select Harvest/Sow Date for %s:", selectedPlant.name()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add listener and set default values
|
* add listener and set default values
|
||||||
* {@inheritDoc}
|
|
||||||
* @param location location
|
|
||||||
* @param resources resources
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@FXML
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize() {
|
||||||
clearDatePickerEntries();
|
clearDatePickerEntries();
|
||||||
|
|
||||||
Callback<DatePicker, DateCell> dayCellFactory= getDayCellFactory();
|
Callback<DatePicker, DateCell> dayCellFactory = getDayCellFactory();
|
||||||
datepicker.setDayCellFactory(dayCellFactory);
|
datepicker.setDayCellFactory(dayCellFactory);
|
||||||
datepicker.setEditable(false);
|
datepicker.setEditable(false);
|
||||||
|
|
||||||
save_button.disableProperty().bind(datepicker.valueProperty().isNull());
|
sow_radio.setUserData(GrowthPhaseType.SOW);
|
||||||
|
harvest_radio.setUserData(GrowthPhaseType.HARVEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable save button when date picker is empty
|
||||||
|
* @param saveButton {@link Button} to be disabled
|
||||||
|
*/
|
||||||
|
public void initSaveButton(Button saveButton) {
|
||||||
|
saveButton.disableProperty().bind(datepicker.valueProperty().isNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,44 +75,29 @@ public class SelectSowDayController implements Initializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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() {
|
return (datePicker) -> new DateCell() {
|
||||||
|
private final LocalDate today = LocalDate.now();
|
||||||
|
|
||||||
@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;
|
|
||||||
LocalDate today = LocalDate.now();
|
if (item.compareTo(today) > 0 && (!harvest_radio.isSelected() || selectedPlant.sowDateFromHarvestDate(item).compareTo(today) >= 0)) {
|
||||||
if (harvest_radio.isSelected()) {
|
GrowthPhaseType selectedPhase = (GrowthPhaseType) phase_group.getSelectedToggle().getUserData();
|
||||||
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.HARVEST);
|
|
||||||
} else {
|
if (selectedPlant.isDateInPhase(item, selectedPhase)) {
|
||||||
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW);
|
|
||||||
}
|
|
||||||
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 ((harvest_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) {
|
|
||||||
setDisable(true);
|
|
||||||
setStyle("-fx-background-color: #ffc0cb;");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* close date picker window
|
|
||||||
*/
|
|
||||||
private void closeWindow() {
|
|
||||||
Stage stage = (Stage) save_button.getScene().getWindow();
|
|
||||||
stage.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package ch.zhaw.gartenverwaltung;
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
|
||||||
public class Settings {
|
public class Settings {
|
||||||
private HardinessZone currentHardinessZone = HardinessZone.ZONE_8A;
|
private HardinessZone currentHardinessZone = HardinessZone.ZONE_8A;
|
||||||
private static Settings instance;
|
private static Settings instance;
|
||||||
|
private final BooleanProperty showTutorial = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
instance = new Settings();
|
instance = new Settings();
|
||||||
@@ -23,4 +26,16 @@ public class Settings {
|
|||||||
public void setCurrentHardinessZone(HardinessZone currentHardinessZone) {
|
public void setCurrentHardinessZone(HardinessZone currentHardinessZone) {
|
||||||
this.currentHardinessZone = currentHardinessZone;
|
this.currentHardinessZone = currentHardinessZone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setShowTutorial (boolean showTutorial) {
|
||||||
|
this.showTutorial.setValue(showTutorial);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty getShowTutorialProperty() {
|
||||||
|
return this.showTutorial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getShowTutorial() {
|
||||||
|
return this.showTutorial.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class SettingsController implements Initializable {
|
||||||
|
Settings settings = Settings.getInstance();
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<HardinessZone> selectHardinessZone_comboBox;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox showTutorial_checkBox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save selected values to {@link Settings}
|
||||||
|
*/
|
||||||
|
public void saveSettings() {
|
||||||
|
settings.setShowTutorial(showTutorial_checkBox.isSelected());
|
||||||
|
settings.setCurrentHardinessZone(selectHardinessZone_comboBox.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save default values form {@link Settings}
|
||||||
|
* @param location location
|
||||||
|
* @param resources resources
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
showTutorial_checkBox.setSelected(settings.getShowTutorial());
|
||||||
|
selectHardinessZone_comboBox.getItems().addAll(HardinessZone.values());
|
||||||
|
selectHardinessZone_comboBox.setValue(settings.getCurrentHardinessZone());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.DateCell;
|
||||||
|
import javafx.scene.control.DatePicker;
|
||||||
|
import javafx.scene.control.TextArea;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class TaskFormularController implements Initializable {
|
||||||
|
private Crop crop;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextArea description_area;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private DatePicker end_datePicker;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField interval_field;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private DatePicker start_datePicker;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField taskName_field;
|
||||||
|
|
||||||
|
|
||||||
|
public Task returnResult() {
|
||||||
|
Task task = new Task(taskName_field.getText(), description_area.getText(),
|
||||||
|
start_datePicker.getValue(), end_datePicker.getValue(),
|
||||||
|
Integer.parseInt(interval_field.getText()), crop.getCropId().get());
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCorp(Crop crop) {
|
||||||
|
this.crop = crop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskValue(Task task) {
|
||||||
|
taskName_field.setText(task.getName());
|
||||||
|
description_area.setText(task.getDescription());
|
||||||
|
start_datePicker.setValue(task.getStartDate());
|
||||||
|
end_datePicker.setValue(task.getEndDate().orElse(null));
|
||||||
|
if(task.getInterval().orElse(null)!=null) {
|
||||||
|
interval_field.setText(task.getInterval().get().toString());
|
||||||
|
} else {
|
||||||
|
interval_field.setText(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Callback<DatePicker, DateCell> getDayCellFactory() {
|
||||||
|
|
||||||
|
return (datePicker) -> new DateCell() {
|
||||||
|
private final LocalDate today = LocalDate.now();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(LocalDate item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
setDisable(true);
|
||||||
|
setStyle("-fx-background-color: #ffc0cb;");
|
||||||
|
|
||||||
|
if (item.compareTo(today) > 0 && item.compareTo(crop.getStartDate()) > 0) {
|
||||||
|
setDisable(false);
|
||||||
|
setStyle("-fx-background-color: #32CD32;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
start_datePicker.setDayCellFactory(getDayCellFactory());
|
||||||
|
start_datePicker.setEditable(false);
|
||||||
|
|
||||||
|
end_datePicker.setDayCellFactory(getDayCellFactory());
|
||||||
|
end_datePicker.setEditable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
public class TutorialController {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public Button previousPageButton;
|
||||||
|
@FXML
|
||||||
|
public Button nextPageButton;
|
||||||
|
@FXML
|
||||||
|
public StackPane tourPages;
|
||||||
|
public ImageView imgAddNewPlant;
|
||||||
|
public ImageView imgTaskList;
|
||||||
|
public ImageView imgSelectDate;
|
||||||
|
|
||||||
|
private int page = 0;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
switchViews();
|
||||||
|
setButtonAbilities();
|
||||||
|
|
||||||
|
Image placeholder = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||||
|
imgAddNewPlant.setImage(placeholder);
|
||||||
|
imgSelectDate.setImage(placeholder);
|
||||||
|
imgTaskList.setImage(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void viewNextPage() {
|
||||||
|
page++;
|
||||||
|
switchViews();
|
||||||
|
setButtonAbilities();
|
||||||
|
}
|
||||||
|
public void viewPreviousPage() {
|
||||||
|
page--;
|
||||||
|
switchViews();
|
||||||
|
setButtonAbilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAbilities() {
|
||||||
|
previousPageButton.setDisable(page <= 0);
|
||||||
|
nextPageButton.setDisable(page >= tourPages.getChildren().size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchViews() {
|
||||||
|
tourPages.getChildren().forEach(node -> node.setOpacity(0));
|
||||||
|
tourPages.getChildren().get(page).setOpacity(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeTutorial() {
|
||||||
|
Stage root = (Stage) tourPages.getScene().getWindow();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,10 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotates a method to be executed after all dependencies annotates with {@link Inject}
|
||||||
|
* have been injected.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface AfterInject { }
|
public @interface AfterInject { }
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package ch.zhaw.gartenverwaltung.bootstrap;
|
package ch.zhaw.gartenverwaltung.bootstrap;
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.HelloApplication;
|
import ch.zhaw.gartenverwaltung.HelloApplication;
|
||||||
import ch.zhaw.gartenverwaltung.MainFXMLController;
|
|
||||||
import ch.zhaw.gartenverwaltung.io.CropList;
|
import ch.zhaw.gartenverwaltung.io.CropList;
|
||||||
import ch.zhaw.gartenverwaltung.io.JsonCropList;
|
import ch.zhaw.gartenverwaltung.io.JsonCropList;
|
||||||
import ch.zhaw.gartenverwaltung.io.JsonPlantList;
|
import ch.zhaw.gartenverwaltung.io.JsonPlantList;
|
||||||
@@ -13,7 +12,7 @@ import ch.zhaw.gartenverwaltung.models.GardenSchedule;
|
|||||||
import ch.zhaw.gartenverwaltung.models.PlantListModel;
|
import ch.zhaw.gartenverwaltung.models.PlantListModel;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.control.DialogPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
@@ -39,14 +38,19 @@ public class AppLoader {
|
|||||||
|
|
||||||
private final GardenSchedule gardenSchedule = new GardenSchedule(taskList, plantList);
|
private final GardenSchedule gardenSchedule = new GardenSchedule(taskList, plantList);
|
||||||
private final Garden garden = new Garden(gardenSchedule, cropList);
|
private final Garden garden = new Garden(gardenSchedule, cropList);
|
||||||
private final MainFXMLController mainController;
|
|
||||||
|
|
||||||
|
|
||||||
public AppLoader(MainFXMLController mainController) throws IOException {
|
public AppLoader() throws IOException {
|
||||||
this.mainController = mainController;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
public Pane loadPane(String fxmlFile) throws IOException {
|
||||||
Pane pane = panes.get(fxmlFile);
|
Pane pane = panes.get(fxmlFile);
|
||||||
if (pane == null) {
|
if (pane == null) {
|
||||||
@@ -56,6 +60,16 @@ public class AppLoader {
|
|||||||
return pane;
|
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 {
|
public Object loadSceneToStage(String fxmlFile, Stage appendee) throws IOException {
|
||||||
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
|
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
|
||||||
Pane root = loader.load();
|
Pane root = loader.load();
|
||||||
@@ -65,13 +79,45 @@ public class AppLoader {
|
|||||||
return 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 {
|
public void loadAndCacheFxml(String fxmlFile) throws IOException {
|
||||||
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
|
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
|
||||||
AnchorPane anchorPane = loader.load();
|
Pane pane = loader.load();
|
||||||
panes.put(fxmlFile, anchorPane);
|
panes.put(fxmlFile, pane);
|
||||||
annotationInject(loader.getController());
|
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) {
|
public void annotationInject(Object controller) {
|
||||||
Arrays.stream(controller.getClass().getDeclaredFields())
|
Arrays.stream(controller.getClass().getDeclaredFields())
|
||||||
.filter(field -> field.isAnnotationPresent(Inject.class))
|
.filter(field -> field.isAnnotationPresent(Inject.class))
|
||||||
@@ -104,7 +150,6 @@ public class AppLoader {
|
|||||||
case "PlantList" -> plantList;
|
case "PlantList" -> plantList;
|
||||||
case "PlantListModel" -> new PlantListModel(plantList);
|
case "PlantListModel" -> new PlantListModel(plantList);
|
||||||
case "GardenSchedule" -> gardenSchedule;
|
case "GardenSchedule" -> gardenSchedule;
|
||||||
case "MainFXMLController" -> mainController;
|
|
||||||
case "AppLoader" -> this;
|
case "AppLoader" -> this;
|
||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung.bootstrap;
|
||||||
|
|
||||||
|
import javafx.event.Event;
|
||||||
|
import javafx.event.EventType;
|
||||||
|
|
||||||
|
public class ChangeViewEvent extends Event {
|
||||||
|
private final String view;
|
||||||
|
|
||||||
|
public static final EventType<ChangeViewEvent> CHANGE_MAIN_VIEW = new EventType<>("CHANGE_MAIN_VIEW");
|
||||||
|
|
||||||
|
public ChangeViewEvent(EventType<? extends Event> eventType, String view) {
|
||||||
|
super(eventType);
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String view() {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,9 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotates a Field to be injected from the application-dependencies
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.FIELD)
|
@Target(ElementType.FIELD)
|
||||||
public @interface Inject { }
|
public @interface Inject { }
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ public class JsonCropList implements CropList {
|
|||||||
public JsonCropList(URL dataSource) {
|
public JsonCropList(URL dataSource) {
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import java.util.Optional;
|
|||||||
* 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 JsonPlantList implements PlantList {
|
||||||
private final URL dataSource = getClass().getResource("plantdb.json");
|
private final URL dataSource;
|
||||||
|
|
||||||
private HardinessZone currentZone;
|
private HardinessZone currentZone;
|
||||||
private Map<Long, Plant> plantMap = Collections.emptyMap();
|
private Map<Long, Plant> plantMap = Collections.emptyMap();
|
||||||
@@ -44,6 +44,13 @@ 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}.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class JsonTaskList implements TaskList {
|
public class JsonTaskList implements TaskList {
|
||||||
IdProvider idProvider;
|
IdProvider idProvider;
|
||||||
private final URL dataSource = getClass().getResource("taskdb.json");
|
private final URL dataSource;
|
||||||
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
|
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
|
||||||
|
|
||||||
private Map<Long, Task> taskMap = Collections.emptyMap();
|
private Map<Long, Task> taskMap = Collections.emptyMap();
|
||||||
@@ -43,6 +43,13 @@ 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.
|
||||||
@@ -96,9 +103,9 @@ public class JsonTaskList implements TaskList {
|
|||||||
if(taskMap.isEmpty()) {
|
if(taskMap.isEmpty()) {
|
||||||
loadTaskListFromFile();
|
loadTaskListFromFile();
|
||||||
}
|
}
|
||||||
if(task.getId() == 0) {
|
long id = task.getId().orElse(idProvider.incrementAndGet());
|
||||||
task.withId(idProvider.incrementAndGet());
|
|
||||||
}
|
taskMap.put(id, task.withId(id));
|
||||||
writeTaskListToFile();
|
writeTaskListToFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,8 +121,9 @@ public class JsonTaskList implements TaskList {
|
|||||||
if(taskMap.isEmpty()) {
|
if(taskMap.isEmpty()) {
|
||||||
loadTaskListFromFile();
|
loadTaskListFromFile();
|
||||||
}
|
}
|
||||||
if(taskMap.containsKey(task.getId())){
|
Long taskId = task.getId().orElseThrow(IOException::new);
|
||||||
taskMap.remove(task.getId());
|
if(taskMap.containsKey(taskId)){
|
||||||
|
taskMap.remove(taskId);
|
||||||
writeTaskListToFile();
|
writeTaskListToFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +163,7 @@ public class JsonTaskList implements TaskList {
|
|||||||
|
|
||||||
taskMap = result.stream()
|
taskMap = result.stream()
|
||||||
.collect(HashMap::new,
|
.collect(HashMap::new,
|
||||||
(res, task) -> res.put(task.getId(), task),
|
(res, task) -> res.put(task.getId().orElse(0L), task),
|
||||||
(existing, replacement) -> {});
|
(existing, replacement) -> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.util.LinkedList;
|
import java.time.MonthDay;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -42,11 +42,10 @@ 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, int group) {
|
public LocalDate sowDateFromHarvestDate(LocalDate harvestDate) {
|
||||||
return harvestDate.minusDays(timeToHarvest(group));
|
return harvestDate.minusDays(timeToHarvest(lifecycleGroupFromHarvestDate(harvestDate)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,37 +68,43 @@ 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) {
|
||||||
* filter out the given growthPhase out of the lifecycle
|
return lifecycle.stream()
|
||||||
* create list of dates for this growthPhase and return it
|
.filter(growthPhase -> growthPhase.type().equals(GrowthPhaseType.HARVEST) &&
|
||||||
* @param growthPhase the wanted growthPhase
|
dateInRange(harvestDate, growthPhase.startDate(), growthPhase.endDate()))
|
||||||
* @return a list of dates of the current year
|
.map(GrowthPhase::group)
|
||||||
*/
|
.findFirst()
|
||||||
public List<LocalDate> getDateListOfGrowthPhase(GrowthPhaseType growthPhase) {
|
.orElse(0);
|
||||||
List<LocalDate> dates = new LinkedList<>();
|
|
||||||
for (GrowthPhase growth : lifecycle) {
|
|
||||||
if (growth.type().equals(growthPhase)) {
|
|
||||||
dates = addDatesFromMonthDay(growth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dates;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* transform monthDay value of the given growthPhase to localDate
|
* Checks if the given {@link LocalDate} is within a {@link GrowthPhase} of the given {@link GrowthPhaseType}
|
||||||
* return a list of dates from start to end of growth phase
|
*
|
||||||
* @param growthPhase the current growthPhase
|
* @param date The date to check.
|
||||||
* @return a list of dates of the current year
|
* @param phase The {@link GrowthPhaseType} to match against
|
||||||
|
* @return Whether the date is within the given {@link GrowthPhaseType}
|
||||||
*/
|
*/
|
||||||
private List<LocalDate> addDatesFromMonthDay(GrowthPhase growthPhase) {
|
public boolean isDateInPhase(LocalDate date, GrowthPhaseType phase) {
|
||||||
List<LocalDate> dates = new LinkedList<>();
|
return lifecycle.stream()
|
||||||
LocalDate today = LocalDate.now();
|
.filter(growthPhase -> growthPhase.type().equals(phase))
|
||||||
LocalDate start = growthPhase.startDate().atYear(today.getYear());
|
.anyMatch(growthPhase -> dateInRange(date, growthPhase.startDate(), growthPhase.endDate()));
|
||||||
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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import java.util.Optional;
|
|||||||
* May be created using the builder pattern.
|
* May be created using the builder pattern.
|
||||||
*/
|
*/
|
||||||
public class Task {
|
public class Task {
|
||||||
private long id;
|
private Long id;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String description;
|
private final String description;
|
||||||
private final LocalDate startDate;
|
private final LocalDate startDate;
|
||||||
@@ -62,7 +62,7 @@ public class Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
public long getId() { return id; }
|
public Optional<Long> getId() { return Optional.ofNullable(id); }
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
public String getDescription() { return description; }
|
public String getDescription() { return description; }
|
||||||
public LocalDate getStartDate() { return startDate; }
|
public LocalDate getStartDate() { return startDate; }
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class TaskTemplate {
|
|||||||
public void setRelativeEndDate(Integer relativeEndDate) {
|
public void setRelativeEndDate(Integer relativeEndDate) {
|
||||||
this.relativeEndDate = relativeEndDate;
|
this.relativeEndDate = relativeEndDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInterval(Integer interval) {
|
public void setInterval(Integer interval) {
|
||||||
this.interval = interval;
|
this.interval = interval;
|
||||||
}
|
}
|
||||||
@@ -45,13 +46,10 @@ public class TaskTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task generateTask(LocalDate realStartDate, long cropId) {
|
public Task generateTask(LocalDate realStartDate, long cropId) {
|
||||||
Task task = new Task(name, description, realStartDate.plusDays(relativeStartDate), cropId);
|
LocalDate endDate = relativeEndDate != null ? realStartDate.plusDays(relativeEndDate) : null;
|
||||||
if (relativeEndDate != null) {
|
|
||||||
task.withEndDate(realStartDate.plusDays(relativeEndDate));
|
return new Task(name, description, realStartDate.plusDays(relativeStartDate), cropId)
|
||||||
}
|
.withInterval(interval)
|
||||||
if (interval != null) {
|
.withEndDate(endDate);
|
||||||
task.withInterval(interval);
|
|
||||||
}
|
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
<?import javafx.scene.control.ScrollPane?>
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
<?import javafx.scene.image.ImageView?>
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
@@ -12,13 +13,11 @@
|
|||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="785.0" prefWidth="899.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.CropDetailController">
|
||||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="785.0" prefWidth="899.0"
|
|
||||||
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.CropDetailController">
|
|
||||||
<children>
|
<children>
|
||||||
<ScrollPane fitToWidth="true" prefHeight="759.0" prefWidth="664.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<ScrollPane fitToWidth="true" prefHeight="759.0" prefWidth="664.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<content>
|
<content>
|
||||||
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="503.0" prefWidth="897.0">
|
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="781.0" prefWidth="897.0">
|
||||||
<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" />
|
||||||
</padding>
|
</padding>
|
||||||
@@ -80,27 +79,27 @@
|
|||||||
<ImageView fx:id="imageView" fitHeight="300.0" fitWidth="300.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER" />
|
<ImageView fx:id="imageView" fitHeight="300.0" fitWidth="300.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Label text="Growth Phases:">
|
<Label text="Tasks:">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="10.0" />
|
<Insets bottom="10.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</Label>
|
</Label>
|
||||||
<VBox fx:id="growthPhases_vbox" prefHeight="135.0" prefWidth="879.0">
|
<ListView fx:id="taskList_listView" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="877.0" VBox.vgrow="ALWAYS">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="10.0" />
|
<Insets bottom="10.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</VBox>
|
</ListView>
|
||||||
<Button mnemonicParsing="false" onAction="#editTaskList" prefHeight="25.0" prefWidth="92.0" text="Edit Tasklist">
|
<Button fx:id="addTask_button" mnemonicParsing="false" onAction="#addTask" prefHeight="25.0" prefWidth="45.0">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="10.0" />
|
<Insets bottom="10.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</Button>
|
</Button>
|
||||||
<Label text="Pests:" />
|
<Label text="Pests:" />
|
||||||
<VBox fx:id="pests_vbox" prefHeight="200.0" prefWidth="100.0">
|
<ListView fx:id="pests_listView" maxHeight="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="10.0" />
|
<Insets bottom="10.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</VBox>
|
</ListView>
|
||||||
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
|
||||||
<children>
|
<children>
|
||||||
<Label text="Area:">
|
<Label text="Area:">
|
||||||
@@ -113,7 +112,7 @@
|
|||||||
<Insets right="10.0" />
|
<Insets right="10.0" />
|
||||||
</HBox.margin>
|
</HBox.margin>
|
||||||
</Label>
|
</Label>
|
||||||
<Button fx:id="area_button" mnemonicParsing="false" onAction="#setArea" prefHeight="25.0" prefWidth="116.0" text="Add Area" />
|
<Button fx:id="area_button" mnemonicParsing="false" onAction="#setArea" prefHeight="25.0" prefWidth="116.0" text="Set Area" />
|
||||||
</children>
|
</children>
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="10.0" />
|
<Insets bottom="10.0" />
|
||||||
@@ -131,7 +130,7 @@
|
|||||||
<Insets right="10.0" />
|
<Insets right="10.0" />
|
||||||
</HBox.margin>
|
</HBox.margin>
|
||||||
</Label>
|
</Label>
|
||||||
<Button fx:id="location_button" mnemonicParsing="false" onAction="#setLocation" prefHeight="25.0" prefWidth="115.0" text="Add Location" />
|
<Button fx:id="location_button" mnemonicParsing="false" onAction="#setLocation" prefHeight="25.0" prefWidth="115.0" text="Set Location" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
</children>
|
</children>
|
||||||
|
|||||||
@@ -2,42 +2,210 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.Pane?>
|
<?import javafx.scene.layout.Pane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1091.0" prefWidth="1060.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.HomeController">
|
||||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="729.0" prefWidth="1060.0"
|
|
||||||
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.HomeController">
|
|
||||||
<children>
|
<children>
|
||||||
<VBox layoutX="75.0" layoutY="73.0" prefHeight="729.0" prefWidth="1060.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<ScrollPane fitToWidth="true" prefHeight="1157.0" prefWidth="1060.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<content>
|
||||||
|
<VBox prefHeight="1091.0" prefWidth="1058.0">
|
||||||
<children>
|
<children>
|
||||||
<Label text="Gartenverwaltung">
|
<Pane prefHeight="1085.0" prefWidth="1018.0">
|
||||||
|
<children>
|
||||||
|
<VBox prefHeight="1047.0" prefWidth="1019.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Garden Management">
|
||||||
<font>
|
<font>
|
||||||
<Font size="34.0" />
|
<Font size="34.0" />
|
||||||
</font>
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="30.0" />
|
||||||
|
</VBox.margin>
|
||||||
</Label>
|
</Label>
|
||||||
<Label prefHeight="106.0" prefWidth="1040.0" text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." wrapText="true" />
|
<Label alignment="TOP_LEFT" prefHeight="22.0" prefWidth="1039.0" text="This Application was created to help the user manage his or her garden. For this the Application has many functionalities:" wrapText="true">
|
||||||
<Label text="Tutorial">
|
|
||||||
<font>
|
<font>
|
||||||
<Font name="System Bold" size="18.0" />
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label text="Base Functionalities:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<Pane prefHeight="200.0" prefWidth="200.0">
|
<Label text="- The user can select a plant he wants to cultivate.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="62.0" text="- The user can filter the plants according to seasons, hardiness zone and search query">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="62.0" text="- The user can select the harverst or sow date. ">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="102.0" text="- The user can get a detailed information of the plant he wants to harvest.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="122.0" text="- The user can get view the task list of the given plant.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="142.0" text="- The user can get the tasks of the next seven days in the scheduler.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="42.0" text="Advanced Functionalities:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="62.0" text="- The user can edit the task list and add custom tasks.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="212.0" text="- The user can set the area (sqare meter) for the plants.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="232.0" text="- The user can set the location (PLZ) for the plants.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="162.0" text="- The user can set the pesticide which will be used, which will create additonal tasks.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="192.0" text="Weather Forcast:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="212.0" text="- According to the location the weather forcast will crate or delete tasks.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="272.0" text="- The user receives notifications that aditional tasks werde created or some tasks were deleted.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label text="Created by:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
<children>
|
<children>
|
||||||
<VBox prefHeight="200.0" prefWidth="1040.0">
|
<ImageView fx:id="imageViewElias" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Elias Csomor" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
<children>
|
<children>
|
||||||
<Label text="Task 1" wrapText="true" />
|
<ImageView fx:id="imageViewPhilippe" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
<Label text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." wrapText="true" />
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Philippe Giavarini" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewDavid" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="David Guler" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewGian" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Gian-Andrea Hutter" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewRoman" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Roman Schenk" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
</children>
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
</Pane>
|
</Pane>
|
||||||
</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" />
|
||||||
</padding>
|
</padding>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
<children>
|
<children>
|
||||||
<HBox maxWidth="35.0" minHeight="35.0" prefHeight="38.0" prefWidth="1110.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<HBox maxWidth="35.0" minHeight="35.0" prefHeight="38.0" prefWidth="1110.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<children>
|
||||||
<Button fx:id="home_button" mnemonicParsing="false" onAction="#goToHome" prefHeight="38.0" prefWidth="121.0" text="Home" HBox.hgrow="NEVER" />
|
<Button fx:id="home_button" mnemonicParsing="false" onAction="#goToHome" prefHeight="38.0" prefWidth="40.0" HBox.hgrow="NEVER" />
|
||||||
<Button fx:id="plants_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToPlants" prefHeight="38.0" prefWidth="121.0" text="Plants" />
|
|
||||||
<Button fx:id="myGarden_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="My Garden" />
|
<Button fx:id="myGarden_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="My Garden" />
|
||||||
<Button fx:id="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="My Schedule" />
|
<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="tutorial_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#showTutorial" prefHeight="38.0" prefWidth="121.0" text="Tutorial" />
|
||||||
<Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
|
<Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
|
||||||
|
<Button fx:id="settings_button" maxHeight="1.7976931348623157E308" mnemonicParsing="false" onAction="#openSettings" prefHeight="38.0" prefWidth="40.0" HBox.hgrow="NEVER" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<AnchorPane fx:id="mainPane" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0" />
|
<AnchorPane fx:id="mainPane" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0" />
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyGardenController">
|
<AnchorPane fx:id="myGardenRoot" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyGardenController">
|
||||||
<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>
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
<Insets bottom="10.0" />
|
<Insets bottom="10.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</Label>
|
</Label>
|
||||||
<VBox fx:id="myPlants_vbox" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS" />
|
<ListView fx:id="myGarden_listView" maxHeight="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" />
|
||||||
<Button fx:id="addPlant_button" mnemonicParsing="false" onAction="#addPlant" prefHeight="45.0" prefWidth="155.0" text="Add new Plant">
|
<Button fx:id="addPlant_button" mnemonicParsing="false" onAction="#addPlant" prefHeight="45.0" prefWidth="155.0" text="Add new Plant">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets top="10.0" />
|
<Insets top="10.0" />
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
|
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
|
||||||
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
|
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
|
fx:controller="ch.zhaw.gartenverwaltung.PlantsController"
|
||||||
|
fx:id="plantsRoot">
|
||||||
<children>
|
<children>
|
||||||
<SplitPane dividerPositions="0.7377363661277062" layoutX="539.0" layoutY="266.0" prefHeight="853.0" prefWidth="1219.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<SplitPane dividerPositions="0.7377363661277062" layoutX="539.0" layoutY="266.0" prefHeight="853.0" prefWidth="1219.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<items>
|
<items>
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
<Font name="System Bold" size="17.0" />
|
<Font name="System Bold" size="17.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<TitledPane animated="false" text="Saison">
|
<TitledPane animated="false" text="Seasons">
|
||||||
<content>
|
<content>
|
||||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
||||||
<children>
|
<children>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
<?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?>
|
||||||
@@ -16,20 +14,19 @@
|
|||||||
<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">
|
<RadioButton fx:id="sow_radio" mnemonicParsing="false" selected="true" text="Sow" toggleGroup="$phase_group">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets bottom="10.0" />
|
<Insets bottom="10.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
<toggleGroup>
|
<toggleGroup>
|
||||||
<ToggleGroup fx:id="group" />
|
<ToggleGroup fx:id="phase_group" />
|
||||||
</toggleGroup>
|
</toggleGroup>
|
||||||
</RadioButton>
|
</RadioButton>
|
||||||
<RadioButton fx:id="harvest_radio" mnemonicParsing="false" text="Harvest" toggleGroup="$group" />
|
<RadioButton fx:id="harvest_radio" mnemonicParsing="false" text="Harvest" toggleGroup="$phase_group" />
|
||||||
</children>
|
</children>
|
||||||
<HBox.margin>
|
<HBox.margin>
|
||||||
<Insets top="10.0" />
|
<Insets top="10.0" />
|
||||||
@@ -38,16 +35,6 @@
|
|||||||
<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" />
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="80.0" prefWidth="374.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.SettingsController">
|
||||||
|
<children>
|
||||||
|
<VBox layoutX="14.0" layoutY="14.0" prefHeight="73.0" prefWidth="374.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<HBox prefHeight="23.0" prefWidth="334.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Show Tutorial Menu" HBox.hgrow="ALWAYS" />
|
||||||
|
<CheckBox fx:id="showTutorial_checkBox" mnemonicParsing="false" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets left="10.0" right="10.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="20.0" layoutY="20.0" prefHeight="23.0" prefWidth="334.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Select Default Hardiness Zone" HBox.hgrow="ALWAYS" />
|
||||||
|
<ComboBox fx:id="selectHardinessZone_comboBox" prefWidth="150.0" promptText="Hardniness Zone" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.DatePicker?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="259.0" prefWidth="390.0" xmlns="http://javafx.com/javafx/17"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TaskFormularController">
|
||||||
|
<children>
|
||||||
|
<VBox layoutX="14.0" layoutY="14.0" prefHeight="272.0" prefWidth="390.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Task name:" HBox.hgrow="ALWAYS" />
|
||||||
|
<TextField fx:id="taskName_field" promptText="Task Name" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox prefHeight="77.0" prefWidth="350.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Description:" HBox.hgrow="ALWAYS" />
|
||||||
|
<TextArea fx:id="description_area" prefHeight="73.0" prefWidth="206.0" promptText="Description" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="30.0" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Start Date:" HBox.hgrow="ALWAYS" />
|
||||||
|
<DatePicker fx:id="start_datePicker" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="143.0" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="End Date:" HBox.hgrow="ALWAYS" />
|
||||||
|
<DatePicker fx:id="end_datePicker" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="30.0" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Interval:" HBox.hgrow="ALWAYS" />
|
||||||
|
<TextField fx:id="interval_field" promptText="Interval (e.g. 0, 1, 3 ...)" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.Separator?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
<?import javafx.scene.text.Text?>
|
||||||
|
<?import javafx.scene.text.TextFlow?>
|
||||||
|
|
||||||
|
<BorderPane maxHeight="470.0" maxWidth="724.0" minHeight="470.0" minWidth="724.0" prefHeight="470.0" prefWidth="724.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TutorialController">
|
||||||
|
<bottom>
|
||||||
|
<HBox alignment="CENTER" prefHeight="0.0" prefWidth="724.0" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<CheckBox mnemonicParsing="false" text="Don't show again" />
|
||||||
|
<Separator prefWidth="50.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ButtonBar prefHeight="40.0" prefWidth="200.0">
|
||||||
|
<buttons>
|
||||||
|
<Button cancelButton="true" contentDisplay="CENTER" graphicTextGap="5.0" mnemonicParsing="false" text="Close" onAction="#closeTutorial"/>
|
||||||
|
<Button fx:id="previousPageButton" mnemonicParsing="false" text="Previous" onAction="#viewPreviousPage"/>
|
||||||
|
<Button fx:id="nextPageButton" defaultButton="true" mnemonicParsing="false" text="Next" onAction="#viewNextPage" />
|
||||||
|
</buttons>
|
||||||
|
</ButtonBar>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</bottom>
|
||||||
|
<center>
|
||||||
|
<StackPane fx:id="tourPages" prefHeight="150.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<VBox layoutX="30.0" layoutY="26.0" opacity="0.0" prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Crops">
|
||||||
|
<font>
|
||||||
|
<Font size="24.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<Separator prefWidth="200.0">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Separator>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="In the "My Garden" tab, you can see all of the plants you're planning on growing.">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Text>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="90.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="To get started, click on the "Add new Plant" button in the bottom left. This will open a list of all the plants you can grow. Use the search bar at the top, the filters on the left, or simply browse the list until you've found a plant you want to grow in your garden." wrappingWidth="345.80316162109375" />
|
||||||
|
</children>
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fx:id="imgAddNewPlant" fitHeight="98.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="200.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Once you've found what you're looking for, click the "Select Sow/Harvest Date" button at the bottom of the window. In the subsequently shown dialog, you can select the date you'd like to sow or harvest the crop. The date selector shows possible dates in green. Confirm your selection with the "Save" button." />
|
||||||
|
</children>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fx:id="imgSelectDate" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
</VBox>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Tasks">
|
||||||
|
<font>
|
||||||
|
<Font size="24.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<Separator prefWidth="200.0">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Separator>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="In the "My Schedule" tab, you can see all of the tasks you need to complete for a successful harvest.">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Text>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="90.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="You can view the tasks for a specific crop by selecting it from the left sidebar. To see all tasks again, click the "Clear Filter" button at the bottom." wrappingWidth="345.80316162109375" />
|
||||||
|
</children>
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fx:id="imgTaskList" fitHeight="98.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="200.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="You can also add your own custom tasks, by clicking the "Add Task" button. In the subsequently shown dialog, you can enter the corresponding details. Note: If you want to make a task recurring, you need to set both an interval (in days) AND an end date, so the task won't repeat for all eternity." />
|
||||||
|
</children>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children></HBox>
|
||||||
|
</children>
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
<BorderPane.margin>
|
||||||
|
<Insets />
|
||||||
|
</BorderPane.margin>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||||
|
</padding>
|
||||||
|
</StackPane>
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -184,15 +184,14 @@
|
|||||||
"litersPerSqM": 15,
|
"litersPerSqM": 15,
|
||||||
"interval": 4,
|
"interval": 4,
|
||||||
"notes": [
|
"notes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"taskTemplates": [
|
"taskTemplates": [
|
||||||
{
|
{
|
||||||
"name": "hilling",
|
"name": "Plant Sets",
|
||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": 0,
|
"relativeEndDate": 0,
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
"description": "Plant the sets about 5cm deep into the soil.",
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"isOptional": false
|
"isOptional": false
|
||||||
}
|
}
|
||||||
@@ -232,7 +231,6 @@
|
|||||||
"litersPerSqM": 0,
|
"litersPerSqM": 0,
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"notes": [
|
"notes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"taskTemplates": [
|
"taskTemplates": [
|
||||||
|
|||||||
@@ -1,56 +1,56 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id" : 1,
|
"id": 1,
|
||||||
"name" : "sow plant",
|
"name": "sow plant",
|
||||||
"description": "Plant the seeds, crops in de bed.",
|
"description": "Plant the seeds, crops in de bed.",
|
||||||
"startDate" : "2022-05-01",
|
"startDate": "2022-05-01",
|
||||||
"endDate" : "2022-05-01",
|
"endDate": "2022-05-01",
|
||||||
"interval" : 0,
|
"interval": 0,
|
||||||
"cropId" : 0
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 2,
|
"id": 2,
|
||||||
"name" : "water plant",
|
"name": "water plant",
|
||||||
"description": "water the plant, so that the soil is wet around the plant.",
|
"description": "water the plant, so that the soil is wet around the plant.",
|
||||||
"startDate" : "2022-05-01",
|
"startDate": "2022-05-01",
|
||||||
"endDate" : "2022-09-01",
|
"endDate": "2022-09-01",
|
||||||
"interval" : 2,
|
"interval": 2,
|
||||||
"cropId" : 0
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 3,
|
"id": 3,
|
||||||
"name" : "fertilize plant",
|
"name": "fertilize plant",
|
||||||
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
|
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
|
||||||
"startDate" : "2022-06-01",
|
"startDate": "2022-06-01",
|
||||||
"endDate" : "2022-08-01",
|
"endDate": "2022-08-01",
|
||||||
"interval" : 28,
|
"interval": 28,
|
||||||
"cropId" : 0
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 4,
|
"id": 4,
|
||||||
"name" : "covering plant",
|
"name": "covering plant",
|
||||||
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
|
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
|
||||||
"startDate" : "2022-07-01",
|
"startDate": "2022-07-01",
|
||||||
"endDate" : "2022-07-01",
|
"endDate": "2022-07-01",
|
||||||
"interval" : 0,
|
"interval": 0,
|
||||||
"cropId" : 0
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 5,
|
"id": 5,
|
||||||
"name" : "look after plant",
|
"name": "look after plant",
|
||||||
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
|
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
|
||||||
"startDate" : "2022-05-01",
|
"startDate": "2022-05-01",
|
||||||
"endDate" : "2022-09-01",
|
"endDate": "2022-09-01",
|
||||||
"interval" : 5,
|
"interval": 5,
|
||||||
"cropId" : 0
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 6,
|
"id": 6,
|
||||||
"name" : "harvest plant",
|
"name": "harvest plant",
|
||||||
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
||||||
"startDate" : "2022-09-01",
|
"startDate": "2022-09-01",
|
||||||
"endDate" : "2022-09-01",
|
"endDate": "2022-09-01",
|
||||||
"interval" : 0,
|
"interval": 0,
|
||||||
"cropId" : 0
|
"cropId": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -28,8 +28,8 @@ public class JsonCropListTest {
|
|||||||
/**
|
/**
|
||||||
* Files to isolate the test-units
|
* Files to isolate the test-units
|
||||||
*/
|
*/
|
||||||
private final URL dbDataSource = this.getClass().getResource("user-crops.json");
|
private final URL dbDataSource = this.getClass().getResource("test-user-crops.json");
|
||||||
private final URL testFile = this.getClass().getResource("test-user-crops.json");
|
private final URL testFile = this.getClass().getResource("template-user-crops.json");
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void connectToDb() throws URISyntaxException, IOException {
|
void connectToDb() throws URISyntaxException, IOException {
|
||||||
|
|||||||
@@ -8,17 +8,22 @@ 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 JsonPlantListTest {
|
public class JsonPlantListTest {
|
||||||
|
private final URL testFile = this.getClass().getResource("test-plantdb.json");
|
||||||
PlantList testDatabase;
|
PlantList testDatabase;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void connectToDb() {
|
void connectToDb() {
|
||||||
testDatabase = new JsonPlantList();
|
assertNotNull(testFile);
|
||||||
|
testDatabase = new JsonPlantList(testFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,27 +18,27 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||||||
public class JsonTaskListTest {
|
public class JsonTaskListTest {
|
||||||
|
|
||||||
TaskList testDatabase;
|
TaskList testDatabase;
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
private final URL dbDataSource = this.getClass().getResource("taskdb.json");
|
private final URL dbDataSource = this.getClass().getResource("test-taskdb.json");
|
||||||
private final URL testFile = this.getClass().getResource("test-taskdb.json");
|
private final URL testFile = this.getClass().getResource("template-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();
|
testDatabase = new JsonTaskList(dbDataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Check if results are retrieved completely")
|
@DisplayName("Check if results are retrieved completely")
|
||||||
void getTasks() {
|
void getTasks() {
|
||||||
|
|
||||||
List<Task> taskList = null;
|
List<Task> taskList;
|
||||||
try {
|
try {
|
||||||
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
|
taskList = testDatabase.getTaskList(LocalDate.parse("2022-04-30", formatter),
|
||||||
LocalDate.parse("31.05.2022", formatter));
|
LocalDate.parse("2022-05-31", formatter));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ public class JsonTaskListTest {
|
|||||||
Task task = new Task("Testtask", "This is a test Task.", LocalDate.parse("01.05.2022", 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 = null;
|
List<Task> taskList;
|
||||||
try {
|
try {
|
||||||
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
|
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
|
||||||
LocalDate.parse("31.05.2022", formatter));
|
LocalDate.parse("31.05.2022", formatter));
|
||||||
@@ -72,13 +72,14 @@ public class JsonTaskListTest {
|
|||||||
@Test
|
@Test
|
||||||
@DisplayName("Remove task.")
|
@DisplayName("Remove task.")
|
||||||
void removeTask() {
|
void removeTask() {
|
||||||
Task task = new Task("Dummy", "Dummy", LocalDate.parse("31.05.2022", formatter), 1).withId(2);
|
Task task = new Task("Dummy", "Dummy", LocalDate.parse("2022-05-31", formatter), 1)
|
||||||
|
.withId(2);
|
||||||
try {
|
try {
|
||||||
testDatabase.removeTask(task);
|
testDatabase.removeTask(task);
|
||||||
List<Task> taskList = null;
|
List<Task> taskList;
|
||||||
|
|
||||||
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
|
taskList = testDatabase.getTaskList(LocalDate.parse("2022-04-30", formatter),
|
||||||
LocalDate.parse("31.05.2022", formatter));
|
LocalDate.parse("2022-05-31", formatter));
|
||||||
|
|
||||||
Assertions.assertEquals(2, taskList.size());
|
Assertions.assertEquals(2, taskList.size());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -90,7 +91,7 @@ public class JsonTaskListTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getTaskForCrop() {
|
void getTaskForCrop() {
|
||||||
List<Task> taskList = null;
|
List<Task> taskList;
|
||||||
try {
|
try {
|
||||||
taskList = testDatabase.getTaskForCrop(0);
|
taskList = testDatabase.getTaskForCrop(0);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -104,7 +105,7 @@ public class JsonTaskListTest {
|
|||||||
@Disabled("Disabled until removing works")
|
@Disabled("Disabled until removing works")
|
||||||
@Test
|
@Test
|
||||||
void removeTasksForCrop() {
|
void removeTasksForCrop() {
|
||||||
List<Task> taskList = null;
|
List<Task> taskList;
|
||||||
try {
|
try {
|
||||||
testDatabase.removeTasksForCrop(0);
|
testDatabase.removeTasksForCrop(0);
|
||||||
taskList = testDatabase.getTaskForCrop(0);
|
taskList = testDatabase.getTaskForCrop(0);
|
||||||
|
|||||||
@@ -6,17 +6,25 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
public class GardenPlanModelTest {
|
public class GardenPlanModelTest {
|
||||||
|
private final URL dbDataSource = JsonCropListTest.class.getResource("test-user-crops.json");
|
||||||
|
private final URL testFile = JsonCropListTest.class.getResource("template-user-crops.json");
|
||||||
|
|
||||||
CropList cropList;
|
CropList cropList;
|
||||||
List<Crop> exampleCrops;
|
List<Crop> exampleCrops;
|
||||||
Crop exampleCropOnion;
|
Crop exampleCropOnion;
|
||||||
@@ -29,7 +37,7 @@ public class GardenPlanModelTest {
|
|||||||
Garden model;
|
Garden model;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() throws IOException {
|
void setUp() throws IOException, URISyntaxException {
|
||||||
|
|
||||||
|
|
||||||
examplePlantOnion = new Plant(
|
examplePlantOnion = new Plant(
|
||||||
@@ -47,8 +55,8 @@ public class GardenPlanModelTest {
|
|||||||
new GrowthPhase(MonthDay.of(2, 8), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
new GrowthPhase(MonthDay.of(2, 8), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
new GrowthPhase(MonthDay.of(10, 2), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
|
new GrowthPhase(MonthDay.of(10, 2), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
|
||||||
|
|
||||||
exampleCropOnion = new Crop(examplePlantOnion.id(), LocalDate.of(2023,3,1));
|
exampleCropOnion = new Crop(examplePlantOnion.id(), LocalDate.of(2023, 3, 1))
|
||||||
exampleCropOnion.withId(3);
|
.withId(3);
|
||||||
examplePlantCarrot = new Plant(
|
examplePlantCarrot = new Plant(
|
||||||
1,
|
1,
|
||||||
"Early Carrot",
|
"Early Carrot",
|
||||||
@@ -59,41 +67,47 @@ public class GardenPlanModelTest {
|
|||||||
"sandy to loamy, loose soil, free of stones",
|
"sandy to loamy, loose soil, free of stones",
|
||||||
new ArrayList<>(),
|
new ArrayList<>(),
|
||||||
List.of(new GrowthPhase(MonthDay.of(4, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
|
List.of(new GrowthPhase(MonthDay.of(4, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
|
||||||
exampleCropCarrot = new Crop(examplePlantCarrot.id(), LocalDate.now());
|
exampleCropCarrot = new Crop(examplePlantCarrot.id(), LocalDate.now())
|
||||||
exampleCropCarrot.withId(5);
|
.withId(5);
|
||||||
|
|
||||||
exampleCrop1 = new Crop(1, LocalDate.of(2023,2,25));
|
exampleCrop1 = new Crop(1, LocalDate.of(2023, 2, 25))
|
||||||
exampleCrop1.withId(0);
|
.withId(0)
|
||||||
exampleCrop1.withArea(0.5);
|
.withArea(0.5);
|
||||||
exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1));
|
exampleCrop2 = new Crop(1, LocalDate.of(2023, 3, 1))
|
||||||
exampleCrop2.withId(1);
|
.withId(1)
|
||||||
exampleCrop2.withArea(0.5);
|
.withArea(0.5);
|
||||||
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,1));
|
exampleCrop3 = new Crop(0, LocalDate.of(2023, 3, 1))
|
||||||
exampleCrop3.withId(2);
|
.withId(2)
|
||||||
exampleCrop3.withArea(1.0);
|
.withArea(1.0);
|
||||||
|
|
||||||
exampleCrops = new ArrayList<>();
|
exampleCrops = new ArrayList<>();
|
||||||
exampleCrops.add(exampleCrop1);
|
exampleCrops.add(exampleCrop1);
|
||||||
exampleCrops.add(exampleCrop2);
|
exampleCrops.add(exampleCrop2);
|
||||||
exampleCrops.add(exampleCrop3);
|
exampleCrops.add(exampleCrop3);
|
||||||
cropList = mockGardenPlan(exampleCrops);
|
cropList = mockCropList(exampleCrops);
|
||||||
|
|
||||||
GardenSchedule gardenSchedule = new GardenSchedule(new JsonTaskList(), new JsonPlantList());
|
// Reset Crop "database" before test
|
||||||
model = new Garden(gardenSchedule, cropList);
|
assertNotNull(testFile);
|
||||||
|
assertNotNull(dbDataSource);
|
||||||
|
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
CropList testDatabase = new JsonCropList(dbDataSource);
|
||||||
|
|
||||||
|
GardenSchedule gardenSchedule = mock(GardenSchedule.class);
|
||||||
|
model = new Garden(gardenSchedule, testDatabase);
|
||||||
}
|
}
|
||||||
|
|
||||||
CropList mockGardenPlan(List<Crop> cropList) throws IOException {
|
CropList mockCropList(List<Crop> cropList) throws IOException {
|
||||||
CropList gardenPlan = mock(CropList.class);
|
CropList croplist = mock(CropList.class);
|
||||||
when(gardenPlan.getCrops()).thenReturn(cropList);
|
when(croplist.getCrops()).thenReturn(cropList);
|
||||||
when(gardenPlan.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
|
when(croplist.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
|
||||||
when(gardenPlan.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
|
when(croplist.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
|
||||||
return gardenPlan;
|
return croplist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
||||||
|
|
||||||
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
|
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023, 3, 1));
|
||||||
Optional<Crop> exampleCrop = model.getCrop(2L);
|
Optional<Crop> exampleCrop = model.getCrop(2L);
|
||||||
assertTrue(exampleCrop.isPresent());
|
assertTrue(exampleCrop.isPresent());
|
||||||
assertEquals(model.getCrops().get(2), exampleCrop.get());
|
assertEquals(model.getCrops().get(2), exampleCrop.get());
|
||||||
@@ -105,6 +119,6 @@ public class GardenPlanModelTest {
|
|||||||
exampleCrop1.withId(2);
|
exampleCrop1.withId(2);
|
||||||
exampleCrop1.withArea(1.5);
|
exampleCrop1.withArea(1.5);
|
||||||
model.removeCrop(exampleCrop1);
|
model.removeCrop(exampleCrop1);
|
||||||
assertEquals(2,model.getCrops().size());
|
assertEquals(2, model.getCrops().size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,257 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"name": "Potato",
|
|
||||||
"description": "The potato is a tuber, round or oval, with small white roots called 'eyes', that are growth buds. The size varies depending on the variety; the colour of the skin can be white, yellow or even purple.",
|
|
||||||
"light": 6,
|
|
||||||
"spacing": "35",
|
|
||||||
"soil": "sandy",
|
|
||||||
"image": "potato.jpg",
|
|
||||||
"pests": [
|
|
||||||
{
|
|
||||||
"name": "Rot",
|
|
||||||
"description": "Rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
|
||||||
"measures": "Less water."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lifecycle": [
|
|
||||||
{
|
|
||||||
"startDate": "03-10",
|
|
||||||
"endDate": "04-10",
|
|
||||||
"type": "SOW",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"group": 0,
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 0,
|
|
||||||
"interval": null,
|
|
||||||
"notes": []
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "Germinate",
|
|
||||||
"relativeStartDate": -14,
|
|
||||||
"relativeEndDate": null,
|
|
||||||
"description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
|
|
||||||
"interval": null,
|
|
||||||
"isOptional": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startDate": "04-10",
|
|
||||||
"endDate": "07-10",
|
|
||||||
"type": "PLANT",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"group": 0,
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 25,
|
|
||||||
"interval": 7,
|
|
||||||
"notes": []
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "hilling",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": null,
|
|
||||||
"description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"",
|
|
||||||
"interval": 21,
|
|
||||||
"isOptional": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startDate": "06-10",
|
|
||||||
"endDate": "08-10",
|
|
||||||
"type": "HARVEST",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"group": 0,
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 0,
|
|
||||||
"interval": null,
|
|
||||||
"notes": []
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "Harvest",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": null,
|
|
||||||
"description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.",
|
|
||||||
"interval": null,
|
|
||||||
"isOptional": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "Early Carrot",
|
|
||||||
"description": "Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.",
|
|
||||||
"image": "carrot.jpg",
|
|
||||||
"lifecycle": [
|
|
||||||
{
|
|
||||||
"startDate": "02-20",
|
|
||||||
"endDate": "03-10",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"type": "SOW",
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 15,
|
|
||||||
"interval": 3,
|
|
||||||
"notes": []
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "hilling",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": 0,
|
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
|
||||||
"interval": null,
|
|
||||||
"isOptional": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startDate": "03-10",
|
|
||||||
"endDate": "05-10",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"type": "PLANT",
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 25,
|
|
||||||
"interval": 3,
|
|
||||||
"notes": [
|
|
||||||
"Be careful not to pour water over the leaves, as this will lead to sunburn."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "hilling",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": null,
|
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
|
||||||
"interval": 15,
|
|
||||||
"isOptional": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startDate": "05-10",
|
|
||||||
"endDate": "05-20",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"type": "HARVEST",
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 0,
|
|
||||||
"interval": null,
|
|
||||||
"notes": []
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "Harvesting",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": 14,
|
|
||||||
"description": "When the leaves turn to a yellowish brown. Do not harvest earlier. The plant will show when it's ready.",
|
|
||||||
"interval": null,
|
|
||||||
"isOptional": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"soil": "sandy to loamy, loose soil, free of stones",
|
|
||||||
"spacing": "5,35,2.5",
|
|
||||||
"pests": [
|
|
||||||
{
|
|
||||||
"name": "Rot",
|
|
||||||
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
|
||||||
"measures": "less water"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "Summertime Onion",
|
|
||||||
"description": "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
|
|
||||||
"image": "onion.jpg",
|
|
||||||
"lifecycle": [
|
|
||||||
{
|
|
||||||
"startDate": "03-15",
|
|
||||||
"endDate": "04-10",
|
|
||||||
"type": "SOW",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"group": 0,
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 15,
|
|
||||||
"interval": 4,
|
|
||||||
"notes": [
|
|
||||||
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "hilling",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": 0,
|
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
|
||||||
"interval": null,
|
|
||||||
"isOptional": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startDate": "04-10",
|
|
||||||
"endDate": "07-10",
|
|
||||||
"type": "PLANT",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"group": 0,
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 25,
|
|
||||||
"interval": 3,
|
|
||||||
"notes": [
|
|
||||||
""
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "hilling",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": null,
|
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
|
||||||
"interval": 15,
|
|
||||||
"isOptional": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startDate": "07-10",
|
|
||||||
"endDate": "09-20",
|
|
||||||
"type": "HARVEST",
|
|
||||||
"zone": "ZONE_8A",
|
|
||||||
"group": 0,
|
|
||||||
"wateringCycle": {
|
|
||||||
"litersPerSqM": 0,
|
|
||||||
"interval": null,
|
|
||||||
"notes": [
|
|
||||||
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"taskTemplates": [
|
|
||||||
{
|
|
||||||
"name": "Harvesting",
|
|
||||||
"relativeStartDate": 0,
|
|
||||||
"relativeEndDate": 14,
|
|
||||||
"description": "When ready for harvest, the leaves on your onion plants will start to flop over. This happens at the \"neck\" of the onion and it signals that the plant has stopped growing and is ready for storage. Onions should be harvested soon thereafter",
|
|
||||||
"interval": null,
|
|
||||||
"isOptional": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"soil": "sandy to loamy, loose soil, free of stones",
|
|
||||||
"spacing": "15,30,2",
|
|
||||||
"pests": [
|
|
||||||
{
|
|
||||||
"name": "Rot",
|
|
||||||
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
|
||||||
"measures": "less water"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"cropId": 0,
|
||||||
|
"plantId": 1,
|
||||||
|
"startDate": "2023-02-25",
|
||||||
|
"area": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cropId": 1,
|
||||||
|
"plantId": 1,
|
||||||
|
"startDate": "2023-03-01",
|
||||||
|
"area": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cropId": 2,
|
||||||
|
"plantId": 0,
|
||||||
|
"startDate": "2023-03-25",
|
||||||
|
"area": 1.5
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -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 enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
|
"description": "Take an egg carton and fill it with soil. Put the seedling deep enough so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.",
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"isOptional": false
|
"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,6 +94,7 @@
|
|||||||
"endDate": "03-10",
|
"endDate": "03-10",
|
||||||
"zone": "ZONE_8A",
|
"zone": "ZONE_8A",
|
||||||
"type": "SOW",
|
"type": "SOW",
|
||||||
|
"group": 0,
|
||||||
"wateringCycle": {
|
"wateringCycle": {
|
||||||
"litersPerSqM": 15,
|
"litersPerSqM": 15,
|
||||||
"interval": 3,
|
"interval": 3,
|
||||||
@@ -115,6 +116,7 @@
|
|||||||
"endDate": "05-10",
|
"endDate": "05-10",
|
||||||
"zone": "ZONE_8A",
|
"zone": "ZONE_8A",
|
||||||
"type": "PLANT",
|
"type": "PLANT",
|
||||||
|
"group": 0,
|
||||||
"wateringCycle": {
|
"wateringCycle": {
|
||||||
"litersPerSqM": 25,
|
"litersPerSqM": 25,
|
||||||
"interval": 3,
|
"interval": 3,
|
||||||
@@ -138,6 +140,7 @@
|
|||||||
"endDate": "05-20",
|
"endDate": "05-20",
|
||||||
"zone": "ZONE_8A",
|
"zone": "ZONE_8A",
|
||||||
"type": "HARVEST",
|
"type": "HARVEST",
|
||||||
|
"group": 0,
|
||||||
"wateringCycle": {
|
"wateringCycle": {
|
||||||
"litersPerSqM": 0,
|
"litersPerSqM": 0,
|
||||||
"interval": null,
|
"interval": null,
|
||||||
@@ -1,56 +1,2 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@@ -1,20 +1,2 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
[
|
|
||||||
]
|
|
||||||