diff --git a/build.gradle b/build.gradle index 35ebaa2..8c3b14a 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,8 @@ dependencies { implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4' testImplementation 'org.mockito:mockito-core:4.3.+' + implementation 'com.sun.mail:javax.mail:1.6.2' + } test { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java index 9018a31..1d8ed5a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java @@ -1,22 +1,35 @@ package ch.zhaw.gartenverwaltung; import ch.zhaw.gartenverwaltung.bootstrap.AppLoader; +import ch.zhaw.gartenverwaltung.backgroundtasks.BackgroundTasks; import javafx.application.Application; import javafx.stage.Stage; import java.io.IOException; +import java.util.Timer; public class HelloApplication extends Application { + Timer backGroundTaskTimer = new Timer(); + BackgroundTasks backgroundTasks; + @Override public void start(Stage stage) throws IOException { AppLoader appLoader = new AppLoader(); + backgroundTasks = new BackgroundTasks(appLoader.getTaskList(), appLoader.getGarden(), appLoader.getPlantList()); + backGroundTaskTimer.scheduleAtFixedRate(backgroundTasks, 0, 1000); + appLoader.loadSceneToStage("MainFXML.fxml", stage); stage.setTitle("Gartenverwaltung"); stage.show(); } + @Override + public void stop(){ + backGroundTaskTimer.cancel(); + } + public static void main(String[] args) { launch(); } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/Settings.java b/src/main/java/ch/zhaw/gartenverwaltung/Settings.java index c15454c..462237c 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/Settings.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/Settings.java @@ -1,13 +1,23 @@ package ch.zhaw.gartenverwaltung; +import ch.zhaw.gartenverwaltung.backgroundtasks.email.SmtpCredentials; import ch.zhaw.gartenverwaltung.types.HardinessZone; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; +import java.util.List; + public class Settings { + private static final Settings instance; private HardinessZone currentHardinessZone = HardinessZone.ZONE_8A; - private static Settings instance; private final BooleanProperty showTutorial = new SimpleBooleanProperty(false); + private SmtpCredentials smtpCredentials = new SmtpCredentials("imap.gmail.com", "587", "pm3.hs22.it21b.win.team1@gmail.com", "pm3.hs22.it21b.win.team1@gmail.com", "bisefhhjtrrhtoqr", true); + // Gmail Address: pm3.hs22.it21b.win.team1@gmail.com + // Gmail Passwort: Gartenverwaltung.PM3.2022 + // E-Mail Inbox: https://www.mailinator.com/v4/public/inboxes.jsp?to=pm3.hs22.it21b.win.team1 + private String mailNotificationReceivers = "pm3.hs22.it21b.win.team1@mailinator.com"; + private String mailNotificationSubjectTemplate = "Task %s is due!"; // {0} = Task Name + private String mailNotificationTextTemplate = "Dear user\nYour gardentask %s for plant %s is due at %tF. Don't forget to confirm in your application if the task is done.\nTask description:\n%s"; // {0} = Task Name, {1} = plantname, {2} = nextExecution, {3} = Task description static { instance = new Settings(); @@ -38,4 +48,20 @@ public class Settings { public boolean getShowTutorial() { return this.showTutorial.get(); } + + public SmtpCredentials getSmtpCredentials() { + return smtpCredentials; + } + + public String getMailNotificationReceivers() { + return mailNotificationReceivers; + } + + public String getMailNotificationSubjectTemplate() { + return mailNotificationSubjectTemplate; + } + + public String getMailNotificationTextTemplate() { + return mailNotificationTextTemplate; + } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/BackgroundTasks.java b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/BackgroundTasks.java new file mode 100644 index 0000000..2cb5e2e --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/BackgroundTasks.java @@ -0,0 +1,36 @@ +package ch.zhaw.gartenverwaltung.backgroundtasks; + +import ch.zhaw.gartenverwaltung.io.PlantList; +import ch.zhaw.gartenverwaltung.io.TaskList; +import ch.zhaw.gartenverwaltung.models.Garden; + +import javax.mail.MessagingException; +import java.io.IOException; +import java.util.TimerTask; + +public class BackgroundTasks extends TimerTask { + private final Notifier notifier; + //TODO uncomment: privat final WeatherGardenTaskPlaner weatherGardenTaskPlaner; + + public BackgroundTasks(TaskList taskList, Garden garden, PlantList plantList) { + notifier = new Notifier(taskList, garden, plantList); + //TODO uncomment: weatherGardenTaskPlaner = new WeatherGardenTaskPlaner(taskList); + } + + @Override + public void run() { + // TODO uncomment: weatherGardenTaskPlaner.refreshTasks(); + try { + try { + notifier.sendNotifications(); + } catch (MessagingException e) { + e.printStackTrace(); + // TODO logger + } + } catch (IOException e) { + e.printStackTrace(); + // TODO logger + } + } +} + diff --git a/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/Notifier.java b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/Notifier.java new file mode 100644 index 0000000..c172e83 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/Notifier.java @@ -0,0 +1,61 @@ +package ch.zhaw.gartenverwaltung.backgroundtasks; + +import ch.zhaw.gartenverwaltung.Settings; +import ch.zhaw.gartenverwaltung.backgroundtasks.email.EMailSender; +import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; +import ch.zhaw.gartenverwaltung.io.PlantList; +import ch.zhaw.gartenverwaltung.io.TaskList; +import ch.zhaw.gartenverwaltung.models.Garden; +import ch.zhaw.gartenverwaltung.types.Crop; +import ch.zhaw.gartenverwaltung.types.Task; + +import javax.mail.MessagingException; +import java.io.IOException; +import java.time.LocalDate; + +public class Notifier { + private final TaskList taskList; + private final Garden garden; + private final PlantList plantList; + private final EMailSender eMailSender = new EMailSender(); + + public Notifier(TaskList taskList, Garden garden, PlantList plantList) { + this.taskList = taskList; + this.garden = garden; + this.plantList = plantList; + } + + private void sendNotification(Task task) { + String plantName = "unkown plant"; + try { + if(garden.getCrop(task.getCropId()).isPresent()){ + Crop crop = garden.getCrop(task.getCropId()).get(); + if(plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).isPresent()) { + plantName = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get().name(); + } + } + } catch (IOException | HardinessZoneNotSetException e) { + e.printStackTrace(); + // TODO logger + } + String messageSubject = String.format(Settings.getInstance().getMailNotificationSubjectTemplate(), task.getName()); + String messageText = String.format(Settings.getInstance().getMailNotificationTextTemplate(), task.getName(), plantName, task.getNextExecution(), task.getDescription()); + try { + eMailSender.sendMails(Settings.getInstance().getMailNotificationReceivers(), messageSubject, messageText); + } catch (MessagingException e) { + e.printStackTrace(); + // TODO Logger + } + } + + public void sendNotifications() throws IOException, MessagingException { + System.out.println("sending Notifications"); + for (Task task : taskList.getTaskList(LocalDate.MIN, LocalDate.MAX)) { + if (task.getNextNotification() != null && task.getNextNotification().isBefore(LocalDate.now().minusDays(1))) { + sendNotification(task); + task.setNextNotification(LocalDate.now().plusDays(1)); + taskList.saveTask(task); + } + } + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/email/EMailSender.java b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/email/EMailSender.java new file mode 100644 index 0000000..1fa470c --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/email/EMailSender.java @@ -0,0 +1,40 @@ +package ch.zhaw.gartenverwaltung.backgroundtasks.email; + +import ch.zhaw.gartenverwaltung.Settings; + +import javax.mail.*; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +public class EMailSender { + + + public EMailSender(){ + + } + + public void sendMails(String recipients, String subject, String text) throws MessagingException { + // TODO replace printMail with implementation + printMail(recipients, subject, text); // TODO Remove Printing E-Mail to console to test it + MimeMessage message = new MimeMessage(Settings.getInstance().getSmtpCredentials().getSession()); + message.addHeader("Content-type", "text/HTML; charset=UTF-8"); + message.addHeader("format", "flowed"); + message.addHeader("Content-Transfer-Encoding", "8bit"); + message.setFrom(new InternetAddress(Settings.getInstance().getSmtpCredentials().fromAddress())); + message.setReplyTo(InternetAddress.parse(Settings.getInstance().getSmtpCredentials().fromAddress(), false)); + message.setSubject(subject); + message.setText(text); + message.setSentDate(new Date()); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients, false)); + Transport.send(message); + } + + private void printMail(String receiver, String subject, String text){ + System.out.printf("\nSending E-Mail:\nTo: %s\nSubject: %s\nMessage:\n%s\n", receiver, subject, text); + } + +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/email/SmtpCredentials.java b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/email/SmtpCredentials.java new file mode 100644 index 0000000..5e066ec --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/backgroundtasks/email/SmtpCredentials.java @@ -0,0 +1,41 @@ +package ch.zhaw.gartenverwaltung.backgroundtasks.email; + +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import java.net.Authenticator; +import java.util.Properties; + +public record SmtpCredentials( + String host, + String port, + String fromAddress, + String username, + String password, + boolean startTLS + ) { + + private Properties getProperties() { + Properties properties = new Properties(); + properties.put("mail.smtp.host", host); + properties.put("mail.smtp.port", port); + properties.put("mail.smtp.auth", "true"); + properties.put("mail.smtp.starttls.enable", startTLS ? "true" : "false"); + return properties; + } + + private javax.mail.Authenticator getAuthenticator() { + return new javax.mail.Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }; + } + + public Session getSession() { + return Session.getInstance(getProperties(), getAuthenticator()); + } + + + +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/models/GardenSchedule.java b/src/main/java/ch/zhaw/gartenverwaltung/models/GardenSchedule.java index 96eae4f..f0868be 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/models/GardenSchedule.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/models/GardenSchedule.java @@ -9,7 +9,6 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.function.Consumer; import java.util.stream.Collectors; public class GardenSchedule { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java index f85e5b1..c8b976b 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java @@ -81,8 +81,10 @@ public class Task { public void done(){ if(interval != null && interval != 0 && !nextExecution.plusDays(interval).isAfter(endDate)){ nextExecution = nextExecution.plusDays(interval); + nextNotification = nextExecution; } else { nextExecution = null; + nextNotification = null; } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 44103dd..159fd0b 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -5,6 +5,8 @@ module ch.zhaw.gartenverwaltung { requires com.fasterxml.jackson.datatype.jsr310; requires com.fasterxml.jackson.datatype.jdk8; requires java.logging; + requires java.mail; + opens ch.zhaw.gartenverwaltung to javafx.fxml; opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; @@ -13,4 +15,8 @@ module ch.zhaw.gartenverwaltung { exports ch.zhaw.gartenverwaltung.types; exports ch.zhaw.gartenverwaltung.models; exports ch.zhaw.gartenverwaltung.json; + exports ch.zhaw.gartenverwaltung.backgroundtasks; + opens ch.zhaw.gartenverwaltung.backgroundtasks to javafx.fxml; + exports ch.zhaw.gartenverwaltung.backgroundtasks.email; + opens ch.zhaw.gartenverwaltung.backgroundtasks.email to javafx.fxml; } \ No newline at end of file diff --git a/src/main/resources/META-INF/javamail.default.address.map b/src/main/resources/META-INF/javamail.default.address.map new file mode 100644 index 0000000..e69de29 diff --git a/src/test/resources/ch/zhaw/gartenverwaltung/io/template-taskdb.json b/src/test/resources/ch/zhaw/gartenverwaltung/io/template-taskdb.json index 7728bb0..b10124d 100644 --- a/src/test/resources/ch/zhaw/gartenverwaltung/io/template-taskdb.json +++ b/src/test/resources/ch/zhaw/gartenverwaltung/io/template-taskdb.json @@ -4,6 +4,8 @@ "name" : "sow plant", "description": "Plant the seeds, crops in de bed.", "startDate" : "2022-05-01", + "nextExecution": "2022-05-01", + "nextNotification": "2022-05-01", "endDate" : "2022-05-01", "interval" : 0, "cropId" : 0 @@ -13,6 +15,8 @@ "name" : "water plant", "description": "water the plant, so that the soil is wet around the plant.", "startDate" : "2022-05-01", + "nextExecution": "2022-05-01", + "nextNotification": "2022-05-01", "endDate" : "2022-09-01", "interval" : 2, "cropId" : 0 @@ -22,6 +26,8 @@ "name" : "fertilize plant", "description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture", "startDate" : "2022-06-01", + "nextExecution": "2022-05-01", + "nextNotification": "2022-05-01", "endDate" : "2022-08-01", "interval" : 28, "cropId" : 0 @@ -31,6 +37,8 @@ "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", + "nextExecution": "2022-05-01", + "nextNotification": "2022-05-01", "endDate" : "2022-07-01", "interval" : 0, "cropId" : 0 @@ -40,6 +48,8 @@ "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", + "nextExecution": "2022-05-01", + "nextNotification": "2022-05-01", "endDate" : "2022-09-01", "interval" : 5, "cropId" : 0 @@ -49,6 +59,8 @@ "name" : "harvest plant", "description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ", "startDate" : "2022-09-01", + "nextExecution": "2022-05-01", + "nextNotification": "2022-05-01", "endDate" : "2022-09-01", "interval" : 0, "cropId" : 0