Compare commits

...

2 commits

Author SHA1 Message Date
3ed46c2fa1 #27: WIP
All checks were successful
Quality Check / Linting Check (push) Successful in 12s
Quality Check / Javadoc Check (push) Successful in 21s
2024-05-21 16:31:48 +02:00
203aaf2804 #27: Implementing Alternative Medications for the View
All checks were successful
Quality Check / Linting Check (push) Successful in 12s
Quality Check / Javadoc Check (push) Successful in 20s
2024-05-21 13:37:58 +02:00
12 changed files with 380 additions and 114 deletions

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="Database" uuid="5a5b8be1-080b-4129-b89d-42f1ea832b90">
<data-source source="LOCAL" name="nursingHome.db" uuid="5a5b8be1-080b-4129-b89d-42f1ea832b90">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>

Binary file not shown.

View file

@ -1,17 +1,17 @@
package de.hitec.nhplus.fixtures;
import de.hitec.nhplus.Main;
import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.medication.Ingredient;
import de.hitec.nhplus.medication.Medication;
import de.hitec.nhplus.medication.database.MedicationDao;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import de.hitec.nhplus.Main;
import de.hitec.nhplus.datastorage.DaoFactory;
import de.hitec.nhplus.medication.Ingredient;
import de.hitec.nhplus.medication.Medication;
import de.hitec.nhplus.medication.database.MedicationDao;
/**
* {@link Fixture} for {@link Medication}.
*
@ -20,11 +20,13 @@ import java.util.*;
public class MedicationFixture implements Fixture<Medication> {
private static final String SCHEMA = "/de/hitec/nhplus/medication/database/Medication.sql";
private static final String INGREDIENT_SCHEMA = "/de/hitec/nhplus/medication/database/Medication_Ingredient.sql";
private static final String ALTERNATIVE_SCHEMA = "/de/hitec/nhplus/medication/database/Medication_Alternative.sql";
@Override
public void dropTable(Connection connection) throws SQLException {
connection.createStatement().execute("DROP TABLE IF EXISTS medication");
connection.createStatement().execute("DROP TABLE IF EXISTS medication_ingredient");
connection.createStatement().execute("DROP TABLE IF EXISTS medication_ingredient");
connection.createStatement().execute("DROP TABLE IF EXISTS medication_alternative");
}
@Override
@ -32,16 +34,25 @@ public class MedicationFixture implements Fixture<Medication> {
final InputStream schema = Main.class.getResourceAsStream(SCHEMA);
final InputStream ingredientSchema = Main.class.getResourceAsStream(INGREDIENT_SCHEMA);
final InputStream alterantiveSchema = Main.class.getResourceAsStream(ALTERNATIVE_SCHEMA);
assert schema != null;
assert ingredientSchema != null;
assert alterantiveSchema != null;
String SQL = new Scanner(schema, StandardCharsets.UTF_8)
.useDelimiter("\\A")
.next();
.useDelimiter("\\A")
.next();
String ingredientSQL = ";" + new Scanner(ingredientSchema, StandardCharsets.UTF_8)
.useDelimiter("\\A")
.next();
.useDelimiter("\\A")
.next();
String alternativeSQL = ";" + new Scanner(alterantiveSchema, StandardCharsets.UTF_8)
.useDelimiter("\\A")
.next();
connection.createStatement().execute(SQL);
connection.createStatement().execute(ingredientSQL);
connection.createStatement().execute(alternativeSQL);
}
@ -67,81 +78,87 @@ public class MedicationFixture implements Fixture<Medication> {
Ingredient warfarinnatrium = new Ingredient("Warfarinnatrium");
medications.add(new Medication(
"Metformin",
"AstraZeneca",
List.of(
metforminhydrochlorid,
cellulose,
povidon,
magnesiumstearat
),
"Übelkeit, Durchfall, Laktatazidose (selten)",
"Oral",
100
"Metformin",
"AstraZeneca",
List.of(
metforminhydrochlorid,
cellulose,
povidon,
magnesiumstearat
),
"Übelkeit, Durchfall, Laktatazidose (selten)",
"Oral",
100,
new ArrayList<>()
));
medications.add(new Medication(
"Lisinopril",
"Teva Pharmaceuticals",
List.of(
lisinoprilDihydrat,
mannitol,
calciumphosphat,
magnesiumstearat
),
"Schwindel, trockener Husten",
"Oral",
150
"Lisinopril",
"Teva Pharmaceuticals",
List.of(
lisinoprilDihydrat,
mannitol,
calciumphosphat,
magnesiumstearat
),
"Schwindel, trockener Husten",
"Oral",
150,
new ArrayList<>()
));
medications.add(new Medication(
"Simvastatin",
"Mylan",
List.of(
simvastatin,
laktose,
cellulose,
magnesiumstearat
),
"Muskelschmerzen, Leberprobleme(selten)",
"Oral",
80
"Simvastatin",
"Mylan",
List.of(
simvastatin,
laktose,
cellulose,
magnesiumstearat
),
"Muskelschmerzen, Leberprobleme(selten)",
"Oral",
80,
new ArrayList<>()
));
medications.add(new Medication(
"Enoxaparin",
"Sanofi",
List.of(
enoxaparinNatrium,
benzylalkohol,
wasser
),
"Blutungen, Reaktionen an der Injektionsstelle",
"Unterhautinjektion",
120
"Enoxaparin",
"Sanofi",
List.of(
enoxaparinNatrium,
benzylalkohol,
wasser
),
"Blutungen, Reaktionen an der Injektionsstelle",
"Unterhautinjektion",
120,
new ArrayList<>()
));
medications.add(new Medication(
"Levothyroxin",
"Sandoz",
List.of(
levothyroxinnatrium,
laktose,
staerke,
akaziengummi
),
"Herzrasen, Gewichtsverlust",
"Oral",
90
"Levothyroxin",
"Sandoz",
List.of(
levothyroxinnatrium,
laktose,
staerke,
akaziengummi
),
"Herzrasen, Gewichtsverlust",
"Oral",
90,
new ArrayList<>()
));
medications.add(new Medication(
"Warfarin",
"Apotex Inc.",
List.of(
warfarinnatrium,
laktose,
staerke,
magnesiumstearat
),
"Blutungen, Blutergüsse",
"Oral",
110
"Warfarin",
"Apotex Inc.",
List.of(
warfarinnatrium,
laktose,
staerke,
magnesiumstearat
),
"Blutungen, Blutergüsse",
"Oral",
110,
new ArrayList<>()
));
MedicationDao dao = DaoFactory.getInstance().createMedicationDAO();
Map<String, Medication> medicationsByName = new HashMap<>();

View file

@ -52,6 +52,10 @@ public class AllMedicationController {
private final ObservableList<Medication> medications = FXCollections.observableArrayList();
private MedicationDao dao;
public MedicationDao getDao() {
return dao;
}
/**
* Initialization method that is called after the binding of all the fields.
*/

View file

@ -23,6 +23,7 @@ public class Medication {
private final SimpleStringProperty possibleSideEffects;
private final SimpleStringProperty administrationMethod;
private final SimpleIntegerProperty currentStock;
private final SimpleListProperty<Medication> alternativeMedication;
/**
* This constructor allows instantiating a {@link Medication} object,
@ -37,7 +38,8 @@ public class Medication {
List<Ingredient> ingredients,
String possibleSideEffects,
String administrationMethod,
int currentStock
int currentStock,
List<Medication> alternativeMedication
) {
this.name = new SimpleStringProperty(name);
this.manufacturer = new SimpleStringProperty(manufacturer);
@ -45,6 +47,7 @@ public class Medication {
this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects);
this.administrationMethod = new SimpleStringProperty(administrationMethod);
this.currentStock = new SimpleIntegerProperty(currentStock);
this.alternativeMedication = new SimpleListProperty<>(FXCollections.observableArrayList(alternativeMedication));
}
/**
@ -57,7 +60,8 @@ public class Medication {
List<Ingredient> ingredients,
String possibleSideEffects,
String administrationMethod,
int currentStock
int currentStock,
List<Medication> alternativeMedication
) {
this.id = new SimpleIntegerProperty(id);
this.name = new SimpleStringProperty(name);
@ -66,6 +70,7 @@ public class Medication {
this.possibleSideEffects = new SimpleStringProperty(possibleSideEffects);
this.administrationMethod = new SimpleStringProperty(administrationMethod);
this.currentStock = new SimpleIntegerProperty(currentStock);
this.alternativeMedication = new SimpleListProperty<>(FXCollections.observableArrayList(alternativeMedication));
}
public int getId() {
@ -124,6 +129,18 @@ public class Medication {
this.possibleSideEffects.set(possibleSideEffects);
}
public ObservableList<Medication> getAlternativeMedication() {
return alternativeMedication.get();
}
public SimpleListProperty<Medication> alternativeMedicationProperty() {
return alternativeMedication;
}
public void setAlternativeMedication(ObservableList<Medication> alternativeMedication) {
this.alternativeMedication.set(alternativeMedication);
}
public String getAdministrationMethod() {
return administrationMethod.get();
}
@ -162,6 +179,14 @@ public class Medication {
.add("Possible Side Effects: " + this.getPossibleSideEffects())
.add("Administration Method: " + this.getAdministrationMethod())
.add("Current Stock: " + this.getCurrentStock())
.add("Alternative Medication" + this.getAlternativeMedication())
.toString();
}
public String getComboBoxString() {
return new StringJoiner(System.lineSeparator())
.add("Name: " + this.getName())
.add("Hersteller: " + this.getManufacturer())
.toString();
}
}

View file

@ -0,0 +1,129 @@
package de.hitec.nhplus.medication;
import java.util.List;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Text;
import org.controlsfx.control.SearchableComboBox;
/**
* A custom implementation of the {@link ListCell} for {@link Ingredient}s.
* This implementation contains an automatic resizing of the parent {@link ListView}.
*
* @author Dominik Säume
*/
public class MedicationListCell extends ListCell<Medication> {
private final Button deleteButton;
private SearchableComboBox<Medication> comboBox;
private static final double BUILTIN_BORDER_WIDTH = 1;
private static final double CELL_SPACING = 4;
private static final double CELL_PADDING = 4;
private static final double BUTTON_PADDING_X = 8;
private static final double BUTTON_PADDING_Y = 4;
private final double totalSpacing;
private final List<Medication> allOtherMedications;
public MedicationListCell(List<Medication> allOtherMedications) {
this.allOtherMedications = allOtherMedications;
this.setPadding(new Insets(CELL_PADDING));
comboBox = new SearchableComboBox();
ObservableList<Medication> list = FXCollections.observableArrayList();
list.setAll(allOtherMedications);
comboBox.setItems(list);
comboBox.setPromptText("Alternatve Auswählen");
comboBox.setCellFactory(this::comboBoxFactory);
comboBox.setButtonCell(comboBoxButtonFactory());
comboBox.valueProperty().addListener(this::onComboBoxChange);
deleteButton = new Button("-");
deleteButton.setPadding(new Insets(
BUTTON_PADDING_Y,
BUTTON_PADDING_X,
BUTTON_PADDING_Y,
BUTTON_PADDING_X
));
deleteButton.setOnAction(event -> getListView().getItems().remove(getItem()));
// Calculate Delete Button Width
Text textNode = new Text(deleteButton.getText());
textNode.setFont(deleteButton.getFont());
double calculatedDeleteButtonWidth = textNode.getLayoutBounds().getWidth() + BUTTON_PADDING_X * 2;
totalSpacing = BUILTIN_BORDER_WIDTH * 2 // List View
+ CELL_PADDING * 2
+ CELL_SPACING
+ calculatedDeleteButtonWidth;
}
private void onComboBoxChange(
ObservableValue<? extends Medication> observableValue,
Medication oldValue,
Medication newValue
) {
ListView<Medication> listView = getListView();
double max = listView.lookupAll("*")
.stream()
.filter(node -> node instanceof MedicationListCell)
.mapToDouble(node -> getComboBoxWidth())
.max()
.orElse(0);
listView.setMinWidth(max + totalSpacing);
}
@Override
protected void updateItem(Medication item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
BorderPane cellPane = new BorderPane();
cellPane.setCenter(comboBox);
cellPane.setRight(deleteButton);
BorderPane.setMargin(deleteButton, new Insets(0, 0, 0, CELL_SPACING));
setGraphic(cellPane);
}
}
private double getComboBoxWidth(){
return comboBox.getWidth();
}
private ListCell comboBoxFactory(Object param) {
return new ListCell<Medication>() {
@Override
protected void updateItem(Medication med, boolean empty) {
super.updateItem(med, empty);
setText(
empty || med == null
? null
: med.getComboBoxString()
);
}
};
}
private ListCell comboBoxButtonFactory() {
return new ListCell<Medication>() {
@Override
protected void updateItem(Medication med, boolean empty) {
super.updateItem(med, empty);
setText(
empty || med == null
? comboBox.getPromptText()
: med.getComboBoxString()
);
}
};
}
}

View file

@ -1,6 +1,11 @@
package de.hitec.nhplus.medication;
import de.hitec.nhplus.treatment.Treatment;
import static de.hitec.nhplus.utils.Validator.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@ -11,12 +16,6 @@ import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import static de.hitec.nhplus.utils.Validator.*;
/**
* The controller for creating and editing a specific {@link Medication}.
*
@ -38,21 +37,25 @@ public class MedicationModalController {
public TextArea textAreaPossibleSideEffects;
@FXML
public Button buttonSave;
@FXML
public ListView<Medication> listViewAlternativeMedication;
private Stage stage;
private Medication medication;
private final ObservableList<Ingredient> ingredients = FXCollections.observableArrayList();
private final ObservableList<Medication> alternativeMediaction = FXCollections.observableArrayList();
private AllMedicationController controller;
private boolean isNewMedication = false;
private List<Medication> allOtherMedications;
/**
* Initialization method that is called after the binding of all the fields.
*/
@FXML
public void initialize(
Stage stage,
AllMedicationController controller,
Medication medication
Stage stage,
AllMedicationController controller,
Medication medication
) {
this.stage = stage;
this.controller = controller;
@ -62,25 +65,30 @@ public class MedicationModalController {
} else {
isNewMedication = true;
this.medication = new Medication(
"",
"",
new ArrayList<>(),
"",
"",
0
"",
"",
new ArrayList<>(),
"",
"",
0,
new ArrayList<>()
);
this.buttonSave.setDisable(true);
}
listViewIngredients.setCellFactory(cellData -> new IngredientListCell());
listViewIngredients.setItems(ingredients);
showData();
listViewAlternativeMedication.setCellFactory(cellData -> new MedicationListCell(allOtherMedications));
listViewAlternativeMedication.setItems(alternativeMediaction);
ChangeListener<String> inputMedicationValidationListener = (observableValue, oldText, newText) -> {
boolean isValid = isValidMedicationName(textFieldName.getText())
&& isValidMedicationManufacturer(textFieldManufacturer.getText())
&& isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText())
&& isValidStock(textFieldCurrentStock.getText());
&& isValidMedicationManufacturer(textFieldManufacturer.getText())
&& isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText())
&& isValidStock(textFieldCurrentStock.getText());
this.buttonSave.setDisable(!isValid);
};
@ -96,6 +104,17 @@ public class MedicationModalController {
*/
private void showData() {
ingredients.setAll(medication.getIngredients());
try {
allOtherMedications = controller.getDao().readAll();
if (!isNewMedication) {
allOtherMedications = allOtherMedications
.stream()
.filter(med -> med.getId() != medication.getId())
.toList();
}
} catch (Exception exception) {
exception.printStackTrace();
}
textFieldName.setText(medication.getName());
textFieldManufacturer.setText(medication.getManufacturer());
textFieldAdministrationMethod.setText(medication.getAdministrationMethod());
@ -111,12 +130,12 @@ public class MedicationModalController {
this.medication.setCurrentStock(Integer.parseInt(textFieldCurrentStock.getText()));
this.medication.setPossibleSideEffects(textAreaPossibleSideEffects.getText());
this.medication.setIngredients(ingredients
.stream()
.map(Ingredient::getName)
.distinct()
.filter(Predicate.not(String::isEmpty))
.map(Ingredient::new)
.toList()
.stream()
.map(Ingredient::getName)
.distinct()
.filter(Predicate.not(String::isEmpty))
.map(Ingredient::new)
.toList()
);
if (isNewMedication) {
@ -138,4 +157,16 @@ public class MedicationModalController {
public void handleAddIngredient() {
ingredients.add(new Ingredient(""));
}
public void handleAddAlternativeMedication() {
alternativeMediaction.add(new Medication(
null,
null,
new ArrayList<>(),
null,
null,
-1,
new ArrayList<>()
));
}
}

View file

@ -63,6 +63,18 @@ public class MedicationDao implements Dao<Medication> {
ingredientStatement.setString(2, ingredient.getName());
ingredientStatement.execute();
}
final String alternativeMedicationSQL = """
INSERT INTO medication_alternative
(id, alternativeId)
VALUES (?, ?);
""";
for (Medication alternative : medication.getAlternativeMedication()) {
PreparedStatement alternativeStatement = this.connection.prepareStatement(alternativeMedicationSQL);
alternativeStatement.setInt(1, newId);
alternativeStatement.setInt(2, alternative.getId());
alternativeStatement.execute();
}
}
@Override
@ -82,7 +94,7 @@ public class MedicationDao implements Dao<Medication> {
@Override
public List<Medication> readAll() throws SQLException {
final String SQL = """
SELECT medication.*, medication_ingredient.name
SELECT medication.*, medication_ingredient.name, medication_alternative.alternativeId
FROM medication LEFT JOIN
medication_ingredient ON medication.id = medication_ingredient.id
""";
@ -103,7 +115,8 @@ public class MedicationDao implements Dao<Medication> {
new ArrayList<>(),
result.getString(4),
result.getString(5),
result.getInt(6)
result.getInt(6),
new ArrayList<>()
);
medications.add(medication);
}
@ -188,10 +201,11 @@ public class MedicationDao implements Dao<Medication> {
result.getInt(1),
result.getString(2),
result.getString(3),
List.of(),
new ArrayList<>(),
result.getString(4),
result.getString(5),
result.getInt(6)
result.getInt(6),
new ArrayList<>()
);
List<Ingredient> ingredients = new ArrayList<>();

View file

@ -3,6 +3,7 @@ module de.hitec.nhplus {
requires javafx.fxml;
requires java.sql;
requires org.xerial.sqlitejdbc;
requires org.controlsfx.controls;
exports de.hitec.nhplus;
opens de.hitec.nhplus to javafx.fxml;

View file

@ -49,6 +49,11 @@
minWidth="100.0"
text="Lagerbestand"
/>
<TableColumn
fx:id="columnAlternativeMedication"
minWidth="100.0"
text="Alternative Medikamente"
/>
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>

View file

@ -3,6 +3,7 @@
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import org.controlsfx.control.SearchableComboBox?>
<BorderPane
xmlns="http://javafx.com/javafx/17.0.2-ea"
xmlns:fx="http://javafx.com/fxml/1"
@ -77,16 +78,13 @@
</padding>
<left>
<BorderPane>
<BorderPane.margin>
<Insets right="8"/>
</BorderPane.margin>
<top>
<Label text="Inhaltsstoffe:"/>
</top>
<center>
<ListView fx:id="listViewIngredients" minWidth="200">
<BorderPane.margin>
<Insets top="8"/>
<Insets top="4"/>
</BorderPane.margin>
</ListView>
</center>
@ -105,10 +103,45 @@
</bottom>
</BorderPane>
</left>
<center>
<right>
<BorderPane>
<top>
<Label text="Nebenwirkungen"/>
<Label text="Alternative Medikamente:"/>
</top>
<center>
<ListView fx:id="listViewAlternativeMedication" minWidth="200">
<BorderPane.margin>
<Insets top="4"/>
</BorderPane.margin>
</ListView>
</center>
<bottom>
<AnchorPane>
<BorderPane.margin>
<Insets top="8"/>
</BorderPane.margin>
<Button
onAction="#handleAddAlternativeMedication"
text="+"
AnchorPane.leftAnchor="0"
AnchorPane.rightAnchor="0"
/>
</AnchorPane>
</bottom>
</BorderPane>
</right>
<center>
<BorderPane>
<BorderPane.margin>
<Insets left="8" right="8"/>
</BorderPane.margin>
<top>
<Label text="Nebenwirkungen">
<BorderPane.margin>
<Insets bottom="4"/>
</BorderPane.margin>
</Label>
</top>
<center>
<TextArea fx:id="textAreaPossibleSideEffects"/>

View file

@ -0,0 +1,7 @@
CREATE TABLE medication_alternative
(
id INTEGER NOT NULL ,
alternativeId INTEGER NOT NULL ,
FOREIGN KEY (id) REFERENCES medication (id) ON DELETE CASCADE,
FOREIGN KEY (alternativeId) REFERENCES medication (id) ON DELETE CASCADE
)