Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: activate/deactivate notification #150

Merged
merged 1 commit into from
Jun 4, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2021 Karnak Team and other contributors.
*
* This program and the accompanying materials are made available under the terms of the Eclipse
* Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache
* License, Version 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package org.karnak.backend.constant;

public class DefaultValuesNotification {

public static final String OBJECT_ERROR_PREFIX = "**ERROR**";
public static final String OBJECT_PATTERN = "[Karnak Notification] %s %.30s";
public static final String OBJECT_VALUES = "PatientID,StudyDescription";
public static final String INTERVAL = "45";
}
Original file line number Diff line number Diff line change
@@ -72,6 +72,9 @@ public class DestinationEntity implements Serializable {
private ProjectEntity projectEntity;
private ForwardNodeEntity forwardNodeEntity;

// Activate notification
private boolean activateNotification;

// list of emails (comma separated) used when the images have been sent (or
// partially sent) to the final destination. Note: if an issue appears before
// sending to the final destination then no email is delivered.
@@ -135,6 +138,7 @@ protected DestinationEntity(DestinationType destinationType) {
this.position = null;
this.savePseudonym = null;
this.filterBySOPClasses = false;

this.notify = "";
this.notifyObjectErrorPrefix = "";
this.notifyObjectPattern = "";
@@ -454,6 +458,14 @@ public void setProjectEntity(ProjectEntity projectEntity) {
this.projectEntity = projectEntity;
}

public boolean isActivateNotification() {
return activateNotification;
}

public void setActivateNotification(boolean activateNotification) {
this.activateNotification = activateNotification;
}

/**
* Informs if this object matches with the filter as text.
*
@@ -589,6 +601,7 @@ public boolean equals(Object o) {
&& Objects.equals(position, that.position)
&& Objects.equals(savePseudonym, that.savePseudonym)
&& Objects.equals(pseudonymAsPatientName, that.pseudonymAsPatientName)
&& Objects.equals(activateNotification, that.activateNotification)
&& Objects.equals(notify, that.notify)
&& Objects.equals(notifyObjectErrorPrefix, that.notifyObjectErrorPrefix)
&& Objects.equals(notifyObjectPattern, that.notifyObjectPattern)
@@ -617,6 +630,7 @@ public int hashCode() {
savePseudonym,
pseudonymAsPatientName,
filterBySOPClasses,
activateNotification,
notify,
notifyObjectErrorPrefix,
notifyObjectPattern,
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
import org.dcm4che3.data.Attributes;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.karnak.backend.constant.DefaultValuesNotification;
import org.karnak.backend.data.entity.DestinationEntity;
import org.karnak.backend.data.entity.DicomSourceNodeEntity;
import org.karnak.backend.data.entity.ForwardNodeEntity;
@@ -127,12 +128,16 @@ public GatewaySetUpService(final ForwardNodeRepo forwardNodeRepo) throws Excepti
mailAuthUser = getProperty("MAIL_SMTP_USER", null);
mailAuthPwd = getProperty("MAIL_SMTP_SECRET", null);

String notifyObjectErrorPrefix = getProperty("NOTIFY_OBJECT_ERROR_PREFIX", "**ERROR**");
String notifyObjectErrorPrefix =
getProperty("NOTIFY_OBJECT_ERROR_PREFIX", DefaultValuesNotification.OBJECT_ERROR_PREFIX);
String notifyObjectPattern =
getProperty("NOTIFY_OBJECT_PATTERN", "[Karnak Notification] %s %.30s");
getProperty("NOTIFY_OBJECT_PATTERN", DefaultValuesNotification.OBJECT_PATTERN);
List<String> notifyObjectValues =
Arrays.asList(getProperty("NOTIFY_OBJECT_VALUES", "PatientID,StudyDescription").split(","));
int notifyInterval = StringUtil.getInt(getProperty("NOTIFY_INTERNAL", "45"));
Arrays.asList(
getProperty("NOTIFY_OBJECT_VALUES", DefaultValuesNotification.OBJECT_VALUES)
.split(","));
int notifyInterval =
StringUtil.getInt(getProperty("NOTIFY_INTERNAL", DefaultValuesNotification.INTERVAL));
this.notificationSetUp =
new NotificationSetUp(
notifyObjectErrorPrefix, notifyObjectPattern, notifyObjectValues, notifyInterval);
Original file line number Diff line number Diff line change
@@ -32,20 +32,17 @@ public class FormDICOM extends VerticalLayout {
private TextField port;
private Checkbox useaetdest;

private TextField notify;
private TextField notifyObjectErrorPrefix;
private TextField notifyObjectPattern;
private TextField notifyObjectValues;
private TextField notifyInterval;
private final LayoutDesidentification layoutDesidentification;
private final FilterBySOPClassesForm filterBySOPClassesForm;
private Checkbox activate;
private DestinationCondition destinationCondition;
private final DestinationCondition destinationCondition;
private final NotificationComponent notificationComponent;

public FormDICOM() {
this.layoutDesidentification = new LayoutDesidentification();
this.filterBySOPClassesForm = new FilterBySOPClassesForm();
this.destinationCondition = new DestinationCondition();
this.notificationComponent = new NotificationComponent();
}

public void init(
@@ -55,6 +52,7 @@ public void init(
this.layoutDesidentification.init(this.binder);
this.filterBySOPClassesForm.init(this.binder);
this.destinationCondition.init(this.binder);
notificationComponent.init(this.binder);

setSizeFull();

@@ -63,22 +61,14 @@ public void init(
hostname = new TextField("Hostname");
port = new TextField("Port");
useaetdest = new Checkbox("Use AETitle destination");
notify = new TextField("Notif.: list of emails");
notifyObjectErrorPrefix = new TextField("Notif.: error subject prefix");
notifyObjectPattern = new TextField("Notif.: subject pattern");
notifyObjectValues = new TextField("Notif.: subject values");
notifyInterval = new TextField("Notif.: interval");
activate = new Checkbox("Enable destination");

add(
UIS.setWidthFull(new HorizontalLayout(aeTitle, description)),
destinationCondition,
UIS.setWidthFull(new HorizontalLayout(hostname, port)),
UIS.setWidthFull(new HorizontalLayout(useaetdest)),
UIS.setWidthFull(new HorizontalLayout(notify)),
UIS.setWidthFull(
new HorizontalLayout(
notifyObjectErrorPrefix, notifyObjectPattern, notifyObjectValues, notifyInterval)),
UIS.setWidthFull(notificationComponent),
UIS.setWidthFull(layoutDesidentification),
UIS.setWidthFull(filterBySOPClassesForm),
UIS.setWidthFull(activate),
@@ -102,29 +92,6 @@ private void setElements() {
UIS.setTooltip(
useaetdest,
"if \"true\" then use the destination AETitle as the calling AETitle at the gateway side");

notify.setWidth("100%");

notifyObjectErrorPrefix.setWidth("24%");
UIS.setTooltip(
notifyObjectErrorPrefix,
"Prefix of the email object when containing an issue. Default value: **ERROR**");

notifyObjectPattern.setWidth("24%");
UIS.setTooltip(
notifyObjectPattern,
"Pattern of the email object, see https://dzone.com/articles/java-string-format-examples. Default value: [Karnak Notification] %s %.30s");

notifyObjectValues.setWidth("24%");
UIS.setTooltip(
notifyObjectValues,
"Values injected in the pattern [PatientID StudyDescription StudyDate StudyInstanceUID]. Default value: PatientID,StudyDescription");

notifyInterval.setWidth("18%");
notifyInterval.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
UIS.setTooltip(
notifyInterval,
"Interval in seconds for sending a notification (when no new image is arrived in the archive folder). Default value: 45");
}

private void setBinder() {
@@ -145,10 +112,6 @@ private void setBinder() {
.withValidator(Objects::nonNull, "Port is mandatory")
.withValidator(value -> 1 <= value && value <= 65535, "Port should be between 1 and 65535")
.bind(DestinationEntity::getPort, DestinationEntity::setPort);
binder
.forField(notifyInterval) //
.withConverter(new HStringToIntegerConverter()) //
.bind(DestinationEntity::getNotifyInterval, DestinationEntity::setNotifyInterval);

binder.bindInstanceFields(this);
}
Original file line number Diff line number Diff line change
@@ -14,11 +14,9 @@
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.textfield.TextFieldVariant;
import com.vaadin.flow.data.binder.Binder;
import org.apache.commons.lang3.StringUtils;
import org.karnak.backend.data.entity.DestinationEntity;
import org.karnak.frontend.component.converter.HStringToIntegerConverter;
import org.karnak.frontend.forwardnode.edit.component.ButtonSaveDeleteCancel;
import org.karnak.frontend.kheops.SwitchingAlbumsView;
import org.karnak.frontend.util.UIS;
@@ -31,20 +29,18 @@ public class FormSTOW extends VerticalLayout {
private TextField url;
private TextField urlCredentials;
private TextArea headers;
private TextField notify;
private TextField notifyObjectErrorPrefix;
private TextField notifyObjectPattern;
private TextField notifyObjectValues;
private TextField notifyInterval;

private final FilterBySOPClassesForm filterBySOPClassesForm;
private SwitchingAlbumsView switchingAlbumsView;
private Checkbox activate;
private DestinationCondition destinationCondition;
private final DestinationCondition destinationCondition;
private final NotificationComponent notificationComponent;

public FormSTOW() {
this.layoutDesidentification = new LayoutDesidentification();
this.filterBySOPClassesForm = new FilterBySOPClassesForm();
this.destinationCondition = new DestinationCondition();
this.notificationComponent = new NotificationComponent();
}

public void init(
@@ -54,37 +50,20 @@ public void init(
this.layoutDesidentification.init(this.binder);
this.filterBySOPClassesForm.init(this.binder);
this.destinationCondition.init(binder);
notificationComponent.init(binder);

this.description = new TextField("Description");
this.url = new TextField("URL");
this.urlCredentials = new TextField("URL credentials");
this.headers = new TextArea("Headers");
this.notify = new TextField("Notif.: list of emails");
this.notifyObjectErrorPrefix = new TextField("Notif.: error subject prefix");
this.notifyObjectPattern = new TextField("Notif.: subject pattern");
this.notifyObjectValues = new TextField("Notif.: subject values");
this.notifyInterval = new TextField("Notif.: interval");

this.switchingAlbumsView = new SwitchingAlbumsView();
this.activate = new Checkbox("Enable destination");

add(
UIS.setWidthFull( //
new HorizontalLayout(description)));
add(UIS.setWidthFull(new HorizontalLayout(description)));
add(destinationCondition);
add(
UIS.setWidthFull( //
new HorizontalLayout(url, urlCredentials)));
add(
UIS.setWidthFull( //
headers));
add(
UIS.setWidthFull( //
new HorizontalLayout(notify)));
add(
UIS.setWidthFull( //
new HorizontalLayout(
notifyObjectErrorPrefix, notifyObjectPattern, notifyObjectValues, notifyInterval)));
add(UIS.setWidthFull(new HorizontalLayout(url, urlCredentials)));
add(UIS.setWidthFull(headers));
add(UIS.setWidthFull(notificationComponent));
add(UIS.setWidthFull(layoutDesidentification));
add(UIS.setWidthFull(filterBySOPClassesForm));
add(UIS.setWidthFull(switchingAlbumsView));
@@ -110,43 +89,18 @@ private void setElements() {
UIS.setTooltip(
headers,
"Headers for HTTP request. Example of format:\n<key>Authorization</key>\n<value>Bearer 1v1pwxT4Ww4DCFzyaMt0NP</value>");

notify.setWidth("100%");

notifyObjectErrorPrefix.setWidth("24%");
UIS.setTooltip(
notifyObjectErrorPrefix,
"Prefix of the email object when containing an issue. Default value: **ERROR**");

notifyObjectPattern.setWidth("24%");
UIS.setTooltip(
notifyObjectPattern,
"Pattern of the email object, see https://dzone.com/articles/java-string-format-examples. Default value: [Karnak Notification] %s %.30s");

notifyObjectValues.setWidth("24%");
UIS.setTooltip(
notifyObjectValues,
"Values injected in the pattern [PatientID StudyDescription StudyDate StudyInstanceUID]. Default value: PatientID,StudyDescription");

notifyInterval.setWidth("18%");
notifyInterval.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
UIS.setTooltip(
notifyInterval,
"Interval in seconds for sending a notification (when no new image is arrived in the archive folder). Default value: 45");
}

private void setBinder() {
binder
.forField(url)
.withValidator(StringUtils::isNotBlank, "URL is mandatory")
.bind(DestinationEntity::getUrl, DestinationEntity::setUrl);
binder
.forField(notifyInterval)
.withConverter(new HStringToIntegerConverter())
.bind(DestinationEntity::getNotifyInterval, DestinationEntity::setNotifyInterval);

binder
.forField(switchingAlbumsView)
.bind(DestinationEntity::getKheopsAlbumEntities, DestinationEntity::setKheopsAlbumEntities);

binder.bindInstanceFields(this);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/*
* Copyright (c) 2021 Karnak Team and other contributors.
*
* This program and the accompanying materials are made available under the terms of the Eclipse
* Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache
* License, Version 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package org.karnak.frontend.forwardnode.edit.destination.component;

import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.textfield.TextFieldVariant;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationResult;
import org.apache.commons.lang3.StringUtils;
import org.karnak.backend.constant.DefaultValuesNotification;
import org.karnak.backend.data.entity.DestinationEntity;
import org.karnak.frontend.component.converter.HStringToIntegerConverter;
import org.karnak.frontend.util.UIS;

/** Create a notification component */
public class NotificationComponent extends VerticalLayout {

// Components
private TextField notify;
private TextField notifyObjectErrorPrefix;
private TextField notifyObjectPattern;
private TextField notifyObjectValues;
private TextField notifyInterval;
private Checkbox activateNotification;
private Div notificationInputsDiv;

/** Constructor */
public NotificationComponent() {
// Size
setWidthFull();

// In order to not have a padding around the component
setPadding(false);

// Build notification components
buildComponents();

// Build listeners
buildListeners();

// Add components
addComponents();
}

/** Add components in notification components */
private void addComponents() {
notificationInputsDiv.add(
UIS.setWidthFull(new HorizontalLayout(notify)),
UIS.setWidthFull(
new HorizontalLayout(
notifyObjectErrorPrefix, notifyObjectPattern, notifyObjectValues, notifyInterval)));

add(
UIS.setWidthFull(new HorizontalLayout(activateNotification)),
UIS.setWidthFull(notificationInputsDiv));
}

/** Build listeners on components */
private void buildListeners() {
buildListenerActivateNotification();
}

/** Listener activate notification */
private void buildListenerActivateNotification() {
activateNotification.addValueChangeListener(
event -> {
if (event != null && event.getValue()) {
notificationInputsDiv.setVisible(true);
notify.clear();
notifyObjectErrorPrefix.setValue(DefaultValuesNotification.OBJECT_ERROR_PREFIX);
notifyObjectPattern.setValue(DefaultValuesNotification.OBJECT_PATTERN);
notifyObjectValues.setValue(DefaultValuesNotification.OBJECT_VALUES);
notifyInterval.setValue(DefaultValuesNotification.INTERVAL);
} else {
notificationInputsDiv.setVisible(false);
notify.clear();
notifyObjectErrorPrefix.clear();
notifyObjectPattern.clear();
notifyObjectValues.clear();
notifyInterval.clear();
}
});
}

/** Build components used in Notification component */
private void buildComponents() {
buildNotificationInputsDiv();
buildActivateNotification();
buildNotify();
buildNotifyObjectErrorPrefix();
buildNotifyObjectPattern();
buildNotifyObjectValues();
buildNotifyInterval();
}

/** Notify interval */
private void buildNotifyInterval() {
notifyInterval = new TextField("Notif.: interval");
notifyInterval.setWidth("18%");
notifyInterval.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
UIS.setTooltip(
notifyInterval,
"Interval in seconds for sending a notification (when no new image is arrived in the archive folder). Default value: 45");
}

/** Notify Object Values */
private void buildNotifyObjectValues() {
notifyObjectValues = new TextField("Notif.: subject values");
notifyObjectValues.setWidth("24%");
UIS.setTooltip(
notifyObjectValues,
"Values injected in the pattern [PatientID StudyDescription StudyDate StudyInstanceUID]. Default value: PatientID,StudyDescription");
}

/** Notify Object Pattern */
private void buildNotifyObjectPattern() {
notifyObjectPattern = new TextField("Notif.: subject pattern");
notifyObjectPattern.setWidth("24%");
UIS.setTooltip(
notifyObjectPattern,
"Pattern of the email object, see https://dzone.com/articles/java-string-format-examples. Default value: [Karnak Notification] %s %.30s");
}

/** Notify Object Error Prefix */
private void buildNotifyObjectErrorPrefix() {
notifyObjectErrorPrefix = new TextField("Notif.: error subject prefix");
notifyObjectErrorPrefix.setWidth("24%");
UIS.setTooltip(
notifyObjectErrorPrefix,
"Prefix of the email object when containing an issue. Default value: **ERROR**");
}

/** Notify */
private void buildNotify() {
notify = new TextField("Notif.: list of emails");
notify.setWidth("100%");
notify.getStyle().set("padding-top", "0");
notify.getStyle().set("padding", "0");
}

/** Activate Notification */
private void buildActivateNotification() {
activateNotification = new Checkbox("Activate notification");
// By default deactivate
activateNotification.setValue(false);
}

/** Notification Inputs Div */
private void buildNotificationInputsDiv() {
notificationInputsDiv = new Div();
// By default hide
notificationInputsDiv.setVisible(false);
}

/**
* Init binder for the component
*
* @param binder Binder
*/
public void init(Binder<DestinationEntity> binder) {

// Activate notification
binder
.forField(getActivateNotification())
.bind(
DestinationEntity::isActivateNotification, DestinationEntity::setActivateNotification);

// List of emails
binder
.forField(getNotify())
.withValidator(
(s, valueContext) -> {
if (StringUtils.isBlank(s) && getActivateNotification().getValue()) {
return ValidationResult.error("Should have at least one address email");
}
return ValidationResult.ok();
})
.bind(DestinationEntity::getNotify, DestinationEntity::setNotify);

// Interval
binder
.forField(getNotifyInterval()) //
.withConverter(new HStringToIntegerConverter()) //
.bind(DestinationEntity::getNotifyInterval, DestinationEntity::setNotifyInterval);

// Error Prefix
binder
.forField(getNotifyObjectErrorPrefix())
.bind(
DestinationEntity::getNotifyObjectErrorPrefix,
DestinationEntity::setNotifyObjectErrorPrefix);

// Subject Pattern
binder
.forField(getNotifyObjectPattern())
.bind(DestinationEntity::getNotifyObjectPattern, DestinationEntity::setNotifyObjectPattern);

// Subject Values
binder
.forField(getNotifyObjectValues())
.bind(DestinationEntity::getNotifyObjectValues, DestinationEntity::setNotifyObjectValues);
}

public TextField getNotify() {
return notify;
}

public void setNotify(TextField notify) {
this.notify = notify;
}

public TextField getNotifyObjectErrorPrefix() {
return notifyObjectErrorPrefix;
}

public void setNotifyObjectErrorPrefix(TextField notifyObjectErrorPrefix) {
this.notifyObjectErrorPrefix = notifyObjectErrorPrefix;
}

public TextField getNotifyObjectPattern() {
return notifyObjectPattern;
}

public void setNotifyObjectPattern(TextField notifyObjectPattern) {
this.notifyObjectPattern = notifyObjectPattern;
}

public TextField getNotifyObjectValues() {
return notifyObjectValues;
}

public void setNotifyObjectValues(TextField notifyObjectValues) {
this.notifyObjectValues = notifyObjectValues;
}

public TextField getNotifyInterval() {
return notifyInterval;
}

public void setNotifyInterval(TextField notifyInterval) {
this.notifyInterval = notifyInterval;
}

public Checkbox getActivateNotification() {
return activateNotification;
}

public void setActivateNotification(Checkbox activateNotification) {
this.activateNotification = activateNotification;
}
}
17 changes: 17 additions & 0 deletions src/main/resources/db/changelog/changes/db.changelog-1.3.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-4.0.xsd">
<changeSet author="karnak" id="1.3-1">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="destination" columnName="activate_notification"/>
</not>
</preConditions>
<addColumn tableName="destination">
<column name="activate_notification" type="BOOLEAN" defaultValue='false'>
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>