Compare commits

...
Author SHA1 Message Date
schrom01 27b8d1754e Javadocs and code cleanup 2022-12-11 14:25:57 +01:00
giavaphi dc0830120f small fixes 2022-12-11 07:59:34 +01:00
giavaphi 39bff805ac small fixes 2022-12-11 07:44:03 +01:00
giavaphi 3ebbb0e0e3 small fixes 2022-12-11 07:19:06 +01:00
Roman SchenkandGitHub Enterprise 8a2119028c Merge pull request #86 from schrom01/fix_tasklist_filteringPastTasks
Fix tasklist filtering past tasks
2022-12-10 14:00:24 +01:00
giavaphiandGitHub Enterprise 083d934472 Merge pull request #85 from schrom01/feature_weather
#23 bugfix testclass WeatherGradenTaskPlannerTest and bugfix Task and…
2022-12-10 13:58:26 +01:00
schrom01 1b096035a7 Merge branch 'dev' into fix_tasklist_filteringPastTasks 2022-12-10 13:54:42 +01:00
schrom01 0074d43364 fixed problem "showing task multiple times in in schedule" 2022-12-10 13:52:33 +01:00
Gian-Andrea Hutter 3bda390708 #23 bugfix testclass WeatherGradenTaskPlannerTest and bugfix Task and WeatherGradenTaskPlanner 2022-12-10 13:47:54 +01:00
Roman SchenkandGitHub Enterprise c6952aba55 Merge pull request #81 from schrom01/feature_updaeTaskListViewOnUpdate_M3
show all tasks in scheduler
2022-12-10 13:41:04 +01:00
Roman SchenkandGitHub Enterprise 807a9017ad Merge pull request #82 from schrom01/fix_tasklist_filteringPastTasks
Fix tasklist filtering past tasks
2022-12-10 13:40:55 +01:00
Roman SchenkandGitHub Enterprise c134025408 Merge pull request #83 from schrom01/tests_extended_M3
extending tests and adding integration test
2022-12-10 13:40:46 +01:00
Roman SchenkandGitHub Enterprise 1e95e0ff30 Merge pull request #84 from schrom01/feature_tutorial-screenshots_M3
added screenshots to tutorial
2022-12-10 13:40:38 +01:00
David Guler fb21797040 added screenshots to tutorial 2022-12-10 13:22:40 +01:00
schrom01 5f7d690875 fixed not showing past tasks 2022-12-09 21:27:13 +01:00
giavaphi 96dc0ad827 fix show all tasks 2022-12-09 19:38:22 +01:00
22 changed files with 287 additions and 88 deletions
@@ -12,6 +12,7 @@ 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.application.Platform;
import javafx.beans.property.ListProperty; import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty; import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@@ -133,8 +134,14 @@ public class CropDetailController {
initializeTaskListProperty(crop); initializeTaskListProperty(crop);
TaskList.TaskListObserver taskListObserver = newTaskList -> { TaskList.TaskListObserver taskListObserver = newTaskList -> {
taskListProperty.clear(); Platform.runLater(() -> {
taskListProperty.addAll(gardenSchedule.getTaskListForCrop(crop.getCropId().get())); taskListProperty.clear();
try {
taskListProperty.addAll(gardenSchedule.getTaskListForCrop(crop.getCropId().get()));
} catch (IOException e) {
e.printStackTrace();
}
});
}; };
gardenSchedule.setTaskListObserver(taskListObserver); gardenSchedule.setTaskListObserver(taskListObserver);
@@ -3,15 +3,15 @@ package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject; import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
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.io.TaskList;
import ch.zhaw.gartenverwaltung.models.Garden; import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule; import ch.zhaw.gartenverwaltung.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.application.Platform;
import javafx.beans.property.ListProperty; import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@@ -24,6 +24,7 @@ import javafx.scene.layout.VBox;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -51,9 +52,15 @@ public class MyScheduleController {
@FXML @FXML
private ListView<Crop> scheduledPlants_listview; private ListView<Crop> scheduledPlants_listview;
@FXML
private void showAllTasks(ActionEvent actionEvent) throws IOException {
gardenSchedule.getTasksUpcomingWeek();
scheduledPlants_listview.getSelectionModel().clearSelection();
}
@AfterInject @AfterInject
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void init() { public void init() throws IOException {
setCellFactoryCropListView(); setCellFactoryCropListView();
setCellFactoryTaskListView(); setCellFactoryTaskListView();
scheduledPlants_listview.itemsProperty().bind(garden.getPlantedCrops()); scheduledPlants_listview.itemsProperty().bind(garden.getPlantedCrops());
@@ -61,6 +68,19 @@ public class MyScheduleController {
week_listView.itemsProperty().bind(taskListProperty); week_listView.itemsProperty().bind(taskListProperty);
lookForSelectedListEntries(); lookForSelectedListEntries();
information_label.setText(""); information_label.setText("");
gardenSchedule.getTasksUpcomingWeek();
TaskList.TaskListObserver taskListObserver = newTaskList -> {
Platform.runLater(() -> {
try {
gardenSchedule.getTasksUpcomingWeek();
scheduledPlants_listview.getSelectionModel().clearSelection();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
};
gardenSchedule.setTaskListObserver(taskListObserver);
} }
/** /**
@@ -81,6 +101,8 @@ public class MyScheduleController {
* set cellFactory for the crops. * set cellFactory for the crops.
*/ */
private void setCellFactoryCropListView() { private void setCellFactoryCropListView() {
MultipleSelectionModel<Crop> selectionModel = scheduledPlants_listview.getSelectionModel();
selectionModel.setSelectionMode(SelectionMode.MULTIPLE);
scheduledPlants_listview.setCellFactory(param -> new ListCell<>() { scheduledPlants_listview.setCellFactory(param -> new ListCell<>() {
@Override @Override
protected void updateItem(Crop crop, boolean empty) { protected void updateItem(Crop crop, boolean empty) {
@@ -133,8 +155,6 @@ public class MyScheduleController {
} else { } else {
gardenSchedule.getTasksUpcomingWeek(); gardenSchedule.getTasksUpcomingWeek();
} }
//taskListProperty.clear();
//taskListProperty.addAll(taskLists);
} }
/** /**
@@ -189,6 +209,12 @@ public class MyScheduleController {
alert.setHeaderText("Are you sure you have completed this task?"); alert.setHeaderText("Are you sure you have completed this task?");
alert.setContentText("Confirming that you have completed the task will remove it from the schedule."); alert.setContentText("Confirming that you have completed the task will remove it from the schedule.");
DialogPane dialogPane = alert.getDialogPane();
dialogPane.getStylesheets().add(
Objects.requireNonNull(getClass().getResource("bootstrap/dialogStyle.css")).toExternalForm());
dialogPane.getStyleClass().add("myDialog");
alert.showAndWait() alert.showAndWait()
.ifPresent(buttonType -> { .ifPresent(buttonType -> {
if (buttonType == ButtonType.OK) { if (buttonType == ButtonType.OK) {
@@ -202,5 +228,4 @@ public class MyScheduleController {
}); });
} }
} }
@@ -7,6 +7,7 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.File;
/** /**
* Controller class for the Tutorial.fxml file * Controller class for the Tutorial.fxml file
@@ -22,6 +23,8 @@ public class TutorialController {
public ImageView imgAddNewPlant; public ImageView imgAddNewPlant;
public ImageView imgTaskList; public ImageView imgTaskList;
public ImageView imgSelectDate; public ImageView imgSelectDate;
public ImageView imgAddTaskButton;
public ImageView imgDetailDeleteButtons;
private int page = 0; private int page = 0;
@@ -30,10 +33,27 @@ public class TutorialController {
switchViews(); switchViews();
setButtonAbilities(); setButtonAbilities();
Image placeholder = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png"))); setImageView(imgAddNewPlant, "add-new-plant.png");
imgAddNewPlant.setImage(placeholder); setImageView(imgSelectDate, "select-sow-harvest.png");
imgSelectDate.setImage(placeholder); setImageView(imgDetailDeleteButtons, "details-delete.png");
imgTaskList.setImage(placeholder); setImageView(imgTaskList, "schedule.png");
setImageView(imgAddTaskButton, "add-task.png");
}
/**
* update the given image view with screenshot or placeholder image.
* @param imageView the image view to update
* @param fileName the file name of the source
*/
private void setImageView(ImageView imageView, String fileName) {
File file = new File(String.valueOf(PlantsController.class.getResource(fileName)));
Image image;
if (file.exists()) {
image = new Image(String.valueOf(PlantsController.class.getResource(fileName)));
} else {
image = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
}
imageView.setImage(image);
} }
public void viewNextPage() { public void viewNextPage() {
@@ -17,27 +17,38 @@ import java.util.TimerTask;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/**
* Class with tasks which must be executed periodic and not triggered by user. Method run must be called in a fix interval.
*/
public class BackgroundTasks extends TimerTask { public class BackgroundTasks extends TimerTask {
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName()); private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
private final TaskList taskList; private final TaskList taskList;
private final Notifier notifier; private final Notifier notifier;
private final WeatherGradenTaskPlanner weatherGardenTaskPlaner; private final WeatherGradenTaskPlanner weatherGardenTaskPlaner;
private void movePastTasks() throws IOException {
List<Task> taskList = this.taskList.getTaskList(LocalDate.MIN, LocalDate.now().minusDays(1));
taskList.forEach(task -> {
if (!task.isDone()) {
task.setNextExecution(LocalDate.now());
}
});
}
public BackgroundTasks(TaskList taskList, CropList cropList, PlantList plantList) { public BackgroundTasks(TaskList taskList, CropList cropList, PlantList plantList) {
this.taskList = taskList; this.taskList = taskList;
notifier = new Notifier(taskList, plantList, cropList); notifier = new Notifier(taskList, plantList, cropList);
weatherGardenTaskPlaner = new WeatherGradenTaskPlanner(taskList, plantList, cropList); weatherGardenTaskPlaner = new WeatherGradenTaskPlanner(taskList, plantList, cropList);
} }
/**
* Changes the field "nextExecution" of all tasks if it's in the past to the actual date as long as they are not done.
* @throws IOException if the taskList can't be read.
*/
private void movePastTasks() throws IOException {
List<Task> taskList = this.taskList.getTaskList(LocalDate.MIN.plusDays(1), LocalDate.now().minusDays(1));
for (Task task : taskList) {
if (!task.isDone()) {
task.setNextExecution(LocalDate.now());
this.taskList.saveTask(task);
}
}
}
/**
* Method to call if Tasks should be executed. It calls all Background tasks after each other.
*/
@Override @Override
public void run() { public void run() {
try { try {
@@ -13,6 +13,9 @@ import javax.mail.MessagingException;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
/**
* Class to send Notifications to the user
*/
public class Notifier { public class Notifier {
private final TaskList taskList; private final TaskList taskList;
private final CropList cropList; private final CropList cropList;
@@ -25,6 +28,13 @@ public class Notifier {
this.plantList = plantList; this.plantList = plantList;
} }
/**
* Method to send a Notification to the user for e specific Task
* @param task The task to use for notification
* @throws IOException if tasklist can not be read
* @throws MessagingException if E-Mail can not be sent for any reason
* @throws HardinessZoneNotSetException if hardiness zone is not set in plant list.
*/
private void sendNotification(Task task) throws IOException, MessagingException, HardinessZoneNotSetException { private void sendNotification(Task task) throws IOException, MessagingException, HardinessZoneNotSetException {
String plantName = "unkown plant"; String plantName = "unkown plant";
if(cropList.getCropById((task.getCropId())).isPresent()){ if(cropList.getCropById((task.getCropId())).isPresent()){
@@ -38,6 +48,12 @@ public class Notifier {
eMailSender.sendMails(Settings.getInstance().getMailNotificationReceivers(), messageSubject, messageText); eMailSender.sendMails(Settings.getInstance().getMailNotificationReceivers(), messageSubject, messageText);
} }
/**
* Sends a notification to the user for each task which is due and not done every day as long as it's not done.
* @throws IOException if tasklist can not be read
* @throws MessagingException if E-Mail can not be sent for any reason
* @throws HardinessZoneNotSetException if hardiness zone is not set in plant list.
*/
public void sendNotifications() throws IOException, MessagingException, HardinessZoneNotSetException { public void sendNotifications() throws IOException, MessagingException, HardinessZoneNotSetException {
for (Task task : taskList.getTaskList(LocalDate.MIN, LocalDate.MAX)) { for (Task task : taskList.getTaskList(LocalDate.MIN, LocalDate.MAX)) {
if (task.getNextNotification() != null && task.getNextNotification().isBefore(LocalDate.now().minusDays(1))) { if (task.getNextNotification() != null && task.getNextNotification().isBefore(LocalDate.now().minusDays(1))) {
@@ -10,16 +10,20 @@ import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
/**
* Class to send E-Mails
*/
public class EMailSender { public class EMailSender {
/**
public EMailSender(){ * Method to send E-Mail to one or multiple recipients
* @param recipients recipients E-Mail addresses separated by ";"
} * @param subject Subject of the E-Mail
* @param text E-Mail message Text as rear text or html
* @throws MessagingException If sending the E-Mail fails for any reason
*/
public void sendMails(String recipients, String subject, String text) throws MessagingException { public void sendMails(String recipients, String subject, String text) throws MessagingException {
// TODO replace printMail with implementation printMail(recipients, subject, text);
printMail(recipients, subject, text); // TODO Remove Printing E-Mail to console to test it
MimeMessage message = new MimeMessage(Settings.getInstance().getSmtpCredentials().getSession()); MimeMessage message = new MimeMessage(Settings.getInstance().getSmtpCredentials().getSession());
message.addHeader("Content-type", "text/HTML; charset=UTF-8"); message.addHeader("Content-type", "text/HTML; charset=UTF-8");
message.addHeader("format", "flowed"); message.addHeader("format", "flowed");
@@ -5,6 +5,10 @@ import javax.mail.Session;
import java.net.Authenticator; import java.net.Authenticator;
import java.util.Properties; import java.util.Properties;
/**
* Class to store SMTP Credentials to send E-Mails and create
* corresponding Session Object
*/
public record SmtpCredentials( public record SmtpCredentials(
String host, String host,
String port, String port,
@@ -14,6 +18,10 @@ public record SmtpCredentials(
boolean startTLS boolean startTLS
) { ) {
/**
* Creates a Properties Object with SMTP Server Information
* @return the created Properties Object
*/
private Properties getProperties() { private Properties getProperties() {
Properties properties = new Properties(); Properties properties = new Properties();
properties.put("mail.smtp.host", host); properties.put("mail.smtp.host", host);
@@ -23,6 +31,10 @@ public record SmtpCredentials(
return properties; return properties;
} }
/**
* Creates a javax.mail.Authenticator Object with username and password for SMTP Server
* @return the created javax.mail.Authenticator Object
*/
private javax.mail.Authenticator getAuthenticator() { private javax.mail.Authenticator getAuthenticator() {
return new javax.mail.Authenticator() { return new javax.mail.Authenticator() {
@Override @Override
@@ -32,10 +44,11 @@ public record SmtpCredentials(
}; };
} }
/**
* Method to get the Session Instance with the given SMTP Credentials
* @return the Session Instance
*/
public Session getSession() { public Session getSession() {
return Session.getInstance(getProperties(), getAuthenticator()); return Session.getInstance(getProperties(), getAuthenticator());
} }
} }
@@ -1,5 +1,8 @@
package ch.zhaw.gartenverwaltung.backgroundtasks.weather; package ch.zhaw.gartenverwaltung.backgroundtasks.weather;
/**
* Enum of possible Weather events
*/
public enum SevereWeather { public enum SevereWeather {
FROST,SNOW,HAIL,NO_SEVERE_WEATHER,RAIN FROST,SNOW,HAIL,NO_SEVERE_WEATHER,RAIN
} }
@@ -9,11 +9,14 @@ import ch.zhaw.gartenverwaltung.types.*;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.stream.Collectors.toList;
/** /**
* The WeatherGardenTaskPlanner creates Tasks based on weather events and the rain amount from the last days * The WeatherGardenTaskPlanner creates Tasks based on weather events and the rain amount from the last days
*
*/ */
public class WeatherGradenTaskPlanner { public class WeatherGradenTaskPlanner {
private final TaskList taskList; private final TaskList taskList;
@@ -43,8 +46,7 @@ public class WeatherGradenTaskPlanner {
private void getSevereWeatherEvents() throws IOException { private void getSevereWeatherEvents() throws IOException {
SevereWeather actualWeather = weatherService.causeSevereWeather(1); SevereWeather actualWeather = weatherService.causeSevereWeather(1);
if (SevereWeather.HAIL.equals(actualWeather)) { if (SevereWeather.HAIL.equals(actualWeather)) {
//ToDo creates hail task all 3 seconds, pls fix createPreHailTask();
//createPreHailTask();
} else if (SevereWeather.FROST.equals(actualWeather)) { } else if (SevereWeather.FROST.equals(actualWeather)) {
createPreFrostTask(); createPreFrostTask();
} else if (SevereWeather.SNOW.equals(actualWeather)) { } else if (SevereWeather.SNOW.equals(actualWeather)) {
@@ -63,11 +65,27 @@ public class WeatherGradenTaskPlanner {
*/ */
private void createPreHailTask() throws IOException { private void createPreHailTask() throws IOException {
Task preHailTask = new Task("Hail", List<Crop> actualCrops = cropList.getCrops();
"During a summer Thunderstorm it could hail heavily. THe Hail could damage the crops. To prevent damage cover the plants with a strong tarpaulin",
dateSevereWeather.minusDays(1L),dateSevereWeather.plusDays(1L),1L);
taskList.saveTask(preHailTask);
for (Crop crop : actualCrops) {
Task preHailTask = new Task("Hail",
"During a summer Thunderstorm it could hail heavily. THe Hail could damage the crops. To prevent damage cover the plants with a strong tarpaulin",
dateSevereWeather,dateSevereWeather.plusDays(1L),crop.getCropId().orElse(-1L));
List<Task> actualCropTaskList = taskList.getTaskForCrop(crop.getCropId().orElse(-1L));
List<Task> hailTasklist = actualCropTaskList.stream().filter(task -> task.getName().equals("Hail")).toList();
List<Task> hailTaskListAtDate = new ArrayList<>();
for (Task task : hailTasklist) {
if (task.getStartDate() == (preHailTask.getStartDate())) {
hailTaskListAtDate.add(task);
}
}
if(hailTaskListAtDate.isEmpty() && hailTasklist.isEmpty()){
taskList.saveTask(preHailTask);
}
}
} }
/** /**
@@ -75,10 +93,26 @@ public class WeatherGradenTaskPlanner {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
private void createPreFrostTask() throws IOException { private void createPreFrostTask() throws IOException {
Task preFrostTask = new Task("Frost", List<Crop> actualCrops = cropList.getCrops();
"The temperatur falls below zero degrees, cover especially the root with wool", for (Crop crop : actualCrops) {
dateSevereWeather.minusDays(1L),dateSevereWeather.plusDays(1L),1L);
taskList.saveTask(preFrostTask); Task preFrostTask = new Task("Frost",
"The temperatur falls below zero degrees, cover especially the root with wool",
dateSevereWeather,dateSevereWeather.plusDays(1L),crop.getCropId().orElse(-1L));
List<Task> actualCropTaskList = taskList.getTaskForCrop(crop.getCropId().orElse(-1L));
List<Task> frostTasklist = actualCropTaskList.stream().filter(task -> task.getName().equals("Frost")).toList();
List<Task> frostTaskListAtDate = new ArrayList<>();
for (Task task : frostTasklist) {
if (task.getStartDate() == preFrostTask.getStartDate()) {
frostTaskListAtDate.add(task);
}
}
if(frostTaskListAtDate.isEmpty() && frostTasklist.isEmpty()){
taskList.saveTask(preFrostTask);
}
}
} }
/** /**
@@ -86,10 +120,27 @@ public class WeatherGradenTaskPlanner {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
private void createPreSnowTask() throws IOException { private void createPreSnowTask() throws IOException {
Task preSnowTask = new Task("Snow", List<Crop> actualCrops = cropList.getCrops();
"The weather brings little snowfall. Cover your crops", for (Crop crop : actualCrops) {
dateSevereWeather.minusDays(1L),dateSevereWeather.plusDays(1L),1L);
taskList.saveTask(preSnowTask); Task preSnowTask = new Task("Snow",
"The weather brings little snowfall. Cover your crops",
dateSevereWeather, dateSevereWeather.plusDays(1L), crop.getCropId().orElse(-1L));
List<Task> actualCropTaskList = taskList.getTaskForCrop(crop.getCropId().orElse(-1L));
List<Task> snowTasklist = actualCropTaskList.stream().filter(task -> task.getName().equals("Snow")).toList();
List<Task> snowTaskListAtDate = new ArrayList<>();
for (Task task : snowTasklist) {
if (task.getStartDate() == preSnowTask.getStartDate()) {
snowTaskListAtDate.add(task);
}
}
if(snowTaskListAtDate .isEmpty() && snowTasklist.isEmpty()){
taskList.saveTask(preSnowTask);
}
}
} }
/** /**
@@ -1,7 +1,6 @@
package ch.zhaw.gartenverwaltung.backgroundtasks.weather; package ch.zhaw.gartenverwaltung.backgroundtasks.weather;
/** /**
* The WeatherService is a class to cause weather events for the WeatherGardenTaskPlanner * The WeatherService is a class to cause weather events for the WeatherGardenTaskPlanner
*
*/ */
public class WeatherService { public class WeatherService {
private static final int NO_RAIN = 0; private static final int NO_RAIN = 0;
@@ -9,6 +8,11 @@ public class WeatherService {
private static final int RAIN = 25; private static final int RAIN = 25;
private static final int HEAVY_RAIN = 50; private static final int HEAVY_RAIN = 50;
/**
* Method to simmulate a Weather Service for testing
* @param randomWeather random int. Range: 1-3
* @return the selected SevereWeather
*/
public SevereWeather causeSevereWeather(int randomWeather) { public SevereWeather causeSevereWeather(int randomWeather) {
return switch (randomWeather) { return switch (randomWeather) {
case 1 -> SevereWeather.HAIL; case 1 -> SevereWeather.HAIL;
@@ -19,6 +23,11 @@ public class WeatherService {
} }
/**
* Method to simulate a Weather Service for testing
* @param randomRainAmount random int. Range: 1-4
* @return the selected rain amount
*/
public int causeRainAmount(int randomRainAmount) { public int causeRainAmount(int randomRainAmount) {
return switch (randomRainAmount) { return switch (randomRainAmount) {
case 1 -> NO_RAIN; case 1 -> NO_RAIN;
@@ -152,6 +152,11 @@ public class AppLoader {
}); });
} }
/**
* Method to get any AppDependency
* @param type Class of Dependency
* @return the App dependency
*/
public Object getAppDependency(Class<?> type) { public Object getAppDependency(Class<?> type) {
return dependencies.get(type.getSimpleName()); return dependencies.get(type.getSimpleName());
} }
@@ -1,5 +1,8 @@
package ch.zhaw.gartenverwaltung.io; package ch.zhaw.gartenverwaltung.io;
/**
* Exceptionm which is thrown if Hardiness zone is not set in plant list.
*/
public class HardinessZoneNotSetException extends Exception { public class HardinessZoneNotSetException extends Exception {
public HardinessZoneNotSetException() { public HardinessZoneNotSetException() {
super("HardinessZone must be set to retrieve plants!"); super("HardinessZone must be set to retrieve plants!");
@@ -2,6 +2,9 @@ package ch.zhaw.gartenverwaltung.io;
import java.io.IOException; import java.io.IOException;
/**
* Excption which is thrown if a JSON file has a invalid File format.
*/
class InvalidJsonException extends IOException { class InvalidJsonException extends IOException {
public InvalidJsonException(String reason) { public InvalidJsonException(String reason) {
super(reason); super(reason);
@@ -32,16 +32,6 @@ public class GardenSchedule {
public GardenSchedule(TaskList taskList, PlantList plantList) throws IOException { public GardenSchedule(TaskList taskList, PlantList plantList) throws IOException {
this.taskList = taskList; this.taskList = taskList;
this.plantList = plantList; this.plantList = plantList;
TaskList.TaskListObserver taskListObserver = newTaskList -> {
Platform.runLater(() -> {
try {
getTasksUpcomingWeek();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
};
setTaskListObserver(taskListObserver);
} }
public ListProperty<List<Task>> getWeeklyTaskListProperty() { public ListProperty<List<Task>> getWeeklyTaskListProperty() {
@@ -176,14 +166,12 @@ public class GardenSchedule {
dayTaskList.add(new ArrayList<>()); dayTaskList.add(new ArrayList<>());
final int finalI = i; final int finalI = i;
weekTasks.forEach(task -> { weekTasks.forEach(task -> {
if (task.getNextExecution() == null) { if(task.getNextExecution() != null) {
task.isDone();
} else {
LocalDate checkDate = task.getNextExecution(); LocalDate checkDate = task.getNextExecution();
do { do {
if (date.equals(checkDate) && !date.isAfter(task.getEndDate().orElse(LocalDate.MIN))) { if (date.equals(task.getNextExecution()) || (date.equals(checkDate) && !date.isAfter(task.getEndDate().orElse(LocalDate.MIN)))) {
dayTaskList.get(finalI).add(task); dayTaskList.get(finalI).add(task);
break;
} }
checkDate = checkDate.plusDays(task.getInterval().orElse(0)); checkDate = checkDate.plusDays(task.getInterval().orElse(0));
} while (!(task.getInterval().orElse(0) == 0) && checkDate.isBefore(LocalDate.now().plusDays(listLength))); } while (!(task.getInterval().orElse(0) == 0) && checkDate.isBefore(LocalDate.now().plusDays(listLength)));
@@ -1,5 +1,8 @@
package ch.zhaw.gartenverwaltung.models; package ch.zhaw.gartenverwaltung.models;
/**
* Exception which is thrown if there is no plant in plant Database with the given id.
*/
public class PlantNotFoundException extends Exception { public class PlantNotFoundException extends Exception {
public PlantNotFoundException() { public PlantNotFoundException() {
super("The selected Plant was not found in Database!"); super("The selected Plant was not found in Database!");
@@ -40,7 +40,6 @@ public class Crop {
public Optional<Long> getCropId() { public Optional<Long> getCropId() {
return Optional.ofNullable(cropId); return Optional.ofNullable(cropId);
} }
public long getPlantId() { return plantId; } public long getPlantId() { return plantId; }
public LocalDate getStartDate() { return startDate; } public LocalDate getStartDate() { return startDate; }
public double getArea() { return area; } public double getArea() { return area; }
@@ -63,6 +63,7 @@ public class Task {
this.startDate = startDate; this.startDate = startDate;
nextExecution = startDate; nextExecution = startDate;
this.endDate = endDate; this.endDate = endDate;
this.cropId = cropId;
} }
/** /**
@@ -107,7 +108,7 @@ public class Task {
* @return Whether the Task is within the given range * @return Whether the Task is within the given range
*/ */
public boolean isInTimePeriod(LocalDate searchStartDate, LocalDate searchEndDate) { public boolean isInTimePeriod(LocalDate searchStartDate, LocalDate searchEndDate) {
return endDate.isAfter(searchStartDate) && startDate.isBefore(searchEndDate) || (nextExecution != null && nextExecution.isBefore(searchEndDate) && nextExecution.isAfter(searchStartDate)); return (endDate.isAfter(searchStartDate) && startDate.isBefore(searchEndDate)) || ((nextExecution != null && nextExecution.isBefore(searchEndDate.plusDays(1)) && nextExecution.isAfter(searchStartDate.minusDays(1))));
} }
/** /**
@@ -4,6 +4,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.LocalDate; import java.time.LocalDate;
/**
* Class which represents a Task if the start and enddate is not known yet.
*/
public class TaskTemplate { public class TaskTemplate {
@JsonProperty @JsonProperty
private final String name; private final String name;
@@ -89,7 +89,7 @@
<Insets bottom="10.0" /> <Insets bottom="10.0" />
</VBox.margin> </VBox.margin>
</ListView> </ListView>
<Button styleClass="button-class" fx:id="addTask_button" mnemonicParsing="false" onAction="#addTask" prefHeight="25.0" prefWidth="45.0" VBox.vgrow="NEVER"> <Button styleClass="button-class" fx:id="addTask_button" mnemonicParsing="false" onAction="#addTask" prefHeight="25.0" prefWidth="120.0" VBox.vgrow="NEVER" text="Add Task">
<VBox.margin> <VBox.margin>
<Insets /> <Insets />
</VBox.margin> </VBox.margin>
@@ -1,6 +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.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?> <?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.AnchorPane?>
@@ -19,11 +20,12 @@
</Label> </Label>
<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="541.0" prefWidth="867.0" spacing="10.0" VBox.vgrow="ALWAYS"> <HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="541.0" prefWidth="867.0" spacing="10.0" VBox.vgrow="ALWAYS">
<children> <children>
<ListView fx:id="scheduledPlants_listview" maxWidth="1.7976931348623157E308" prefHeight="522.0" prefWidth="271.0" HBox.hgrow="NEVER"> <VBox prefHeight="497.0" prefWidth="237.0" spacing="10.0" HBox.hgrow="NEVER">
<HBox.margin> <children>
<Insets /> <ListView fx:id="scheduledPlants_listview" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="522.0" prefWidth="271.0" VBox.vgrow="ALWAYS" />
</HBox.margin> <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#showAllTasks" styleClass="button-class" text="Show All Tasks" VBox.vgrow="NEVER" />
</ListView> </children>
</VBox>
<VBox maxWidth="1.7976931348623157E308" prefHeight="537.0" prefWidth="650.0" spacing="10.0" HBox.hgrow="ALWAYS"> <VBox maxWidth="1.7976931348623157E308" prefHeight="537.0" prefWidth="650.0" spacing="10.0" HBox.hgrow="ALWAYS">
<children> <children>
<ListView fx:id="week_listView" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS"> <ListView fx:id="week_listView" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
@@ -22,9 +22,9 @@
<Separator prefWidth="50.0" visible="false" HBox.hgrow="ALWAYS" /> <Separator prefWidth="50.0" visible="false" HBox.hgrow="ALWAYS" />
<ButtonBar prefHeight="40.0" prefWidth="200.0"> <ButtonBar prefHeight="40.0" prefWidth="200.0">
<buttons> <buttons>
<Button styleClass="button-class" cancelButton="true" contentDisplay="CENTER" graphicTextGap="5.0" mnemonicParsing="false" text="Close" onAction="#closeTutorial"/> <Button cancelButton="true" contentDisplay="CENTER" graphicTextGap="5.0" mnemonicParsing="false" onAction="#closeTutorial" styleClass="button-class" text="Close" />
<Button styleClass="button-class" fx:id="previousPageButton" mnemonicParsing="false" text="Previous" onAction="#viewPreviousPage"/> <Button fx:id="previousPageButton" mnemonicParsing="false" onAction="#viewPreviousPage" styleClass="button-class" text="Previous" />
<Button styleClass="button-class" fx:id="nextPageButton" defaultButton="true" mnemonicParsing="false" text="Next" onAction="#viewNextPage" /> <Button fx:id="nextPageButton" defaultButton="true" mnemonicParsing="false" onAction="#viewNextPage" styleClass="button-class" text="Next" />
</buttons> </buttons>
</ButtonBar> </ButtonBar>
</children> </children>
@@ -38,7 +38,7 @@
<children> <children>
<VBox layoutX="30.0" layoutY="26.0" opacity="0.0" prefHeight="200.0" prefWidth="100.0"> <VBox layoutX="30.0" layoutY="26.0" opacity="0.0" prefHeight="200.0" prefWidth="100.0">
<children> <children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Crops"> <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Adding Crops">
<font> <font>
<Font size="24.0" /> <Font size="24.0" />
</font> </font>
@@ -75,7 +75,7 @@
</children> </children>
</TextFlow> </TextFlow>
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" /> <Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
<ImageView fx:id="imgSelectDate" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" /> <ImageView fx:id="imgSelectDate" fitHeight="150.0" fitWidth="400.0" pickOnBounds="true" preserveRatio="true" />
</children> </children>
</HBox> </HBox>
</children> </children>
@@ -83,7 +83,50 @@
<Insets /> <Insets />
</opaqueInsets> </opaqueInsets>
</VBox> </VBox>
<VBox prefHeight="200.0" prefWidth="100.0"> <VBox layoutX="30.0" layoutY="30.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="Once you've added a crop to your garden, it will be displayed in the &quot;My Garden&quot; tab.">
<VBox.margin>
<Insets bottom="20.0" />
</VBox.margin>
</Text>
<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="On the right hand side of the listing, you'll see two buttons.&#10;&#10;Button with the &quot;trash can&quot; icon lets you remove a crop from your garden plan. This will also delete all associated tasks.&#10;&#10;The button on the left will display the details of the crop in a new window." />
</children>
</TextFlow>
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
<ImageView fitHeight="70.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" fx:id="imgDetailDeleteButtons"/>
</children></HBox>
<HBox layoutX="10.0" layoutY="105.0" 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="Here, you can also add your own custom tasks, by clicking the &quot;Add Task&quot; button.&#10;In the subsequently shown dialog, you can enter the corresponding details.&#10;&#10;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="400.0" pickOnBounds="true" preserveRatio="true" fx:id="imgAddTaskButton"/>
</children>
</HBox>
</children>
<opaqueInsets>
<Insets />
</opaqueInsets>
</VBox>
<VBox opacity="0.0" prefHeight="200.0" prefWidth="100.0">
<children> <children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Tasks"> <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Tasks">
<font> <font>
@@ -111,19 +154,9 @@
</HBox.margin> </HBox.margin>
</TextFlow> </TextFlow>
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" /> <Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
<ImageView fx:id="imgTaskList" fitHeight="98.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" /> <ImageView fx:id="imgTaskList" fitHeight="200.0" fitWidth="400.0" pickOnBounds="true" preserveRatio="true" />
</children> </children>
</HBox> </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 &quot;Add Task&quot; button.&#10;In the subsequently shown dialog, you can enter the corresponding details.&#10;&#10;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> </children>
<opaqueInsets> <opaqueInsets>
<Insets /> <Insets />
@@ -52,7 +52,7 @@ public class WeatherGardenTaskPlannerTest {
exampleTask.setNextExecution(LocalDate.now().plusDays(1L)); exampleTask.setNextExecution(LocalDate.now().plusDays(1L));
exampleWeatherTask = new Task("Hail", exampleWeatherTask = new Task("Hail",
"During a summer Thunderstorm it could hail heavily. THe Hail could damage the crops. To prevent damage cover the plants with a strong tarpaulin", "During a summer Thunderstorm it could hail heavily. THe Hail could damage the crops. To prevent damage cover the plants with a strong tarpaulin",
LocalDate.now().minusDays(1L),LocalDate.now().plusDays(1L),3L); LocalDate.now(),LocalDate.now().plusDays(1L),3L);
taskList = new JsonTaskList(testFile); taskList = new JsonTaskList(testFile);