| @@ -7,6 +7,7 @@ import de.uniluebeck.mi.projmi6.db.DBHandler; | |||
| import de.uniluebeck.mi.projmi6.hapi.HL7Receiver; | |||
| import de.uniluebeck.mi.projmi6.hapi.HL7Server; | |||
| import javafx.application.Application; | |||
| import javafx.application.Platform; | |||
| import javafx.beans.property.ReadOnlyStringProperty; | |||
| import javafx.collections.FXCollections; | |||
| import javafx.concurrent.Task; | |||
| @@ -27,17 +28,29 @@ import javafx.scene.text.Text; | |||
| import javafx.stage.Stage; | |||
| import javafx.stage.StageStyle; | |||
| /** | |||
| * The JavaFX application class that starts our application. | |||
| * | |||
| * @author Johannes | |||
| */ | |||
| public class Main extends Application { | |||
| /** | |||
| * The ip adress of the OPS system we wan't to communicate with. | |||
| * Can be set in the settings tab. | |||
| */ | |||
| public static String OPS_IP = "127.0.0.1"; | |||
| public static int OPS_PORT = 1112; | |||
| /** | |||
| * OPS system's port number. | |||
| * Can be changed from the settings tab. | |||
| */ | |||
| public static int OPS_PORT = 1112; | |||
| private HL7Server server; | |||
| /** | |||
| * Cuz building the GUI from FXML is a bit costly, | |||
| * Because building the GUI from FXML is a bit costly | |||
| * and loading the master data takes some time, | |||
| * it's done from its own thread. | |||
| */ | |||
| private Task<Parent> loadMainWindowTask = new Task<Parent>() { | |||
| @@ -101,8 +114,18 @@ public class Main extends Application { | |||
| launch(args); | |||
| } | |||
| /** | |||
| * Server instance that is currently running | |||
| */ | |||
| private HL7Server server; | |||
| /** | |||
| * In the application lifecycle, this method will be called when closing. | |||
| */ | |||
| @Override | |||
| public void stop() throws Exception { | |||
| //Stop the server | |||
| if (server != null) { | |||
| server.stop(); | |||
| server.shutdown(); | |||
| @@ -112,9 +135,6 @@ public class Main extends Application { | |||
| @Override | |||
| public void start(Stage primaryStage) { | |||
| // TODO: Jojo kann das weg? | |||
| // System.out.println(getClass().getClassLoader().getResource("").toExternalForm()); | |||
| primaryStage.getIcons().add(icon); | |||
| Stage loadingMessage = createLoadWindow(loadMainWindowTask.messageProperty()); | |||
| @@ -122,6 +142,7 @@ public class Main extends Application { | |||
| loadMainWindowTask.setOnFailed(event -> { | |||
| loadMainWindowTask.getException().printStackTrace(); | |||
| loadingMessage.close(); | |||
| Platform.exit(); | |||
| }); | |||
| loadMainWindowTask.setOnSucceeded(event -> { | |||
| Parent root = loadMainWindowTask.getValue(); | |||
| @@ -162,6 +183,7 @@ public class Main extends Application { | |||
| Scene scene = new Scene(root, 400, 500); | |||
| Stage stage = new Stage(StageStyle.UNDECORATED); | |||
| stage.setTitle("KIS wird geladen...."); | |||
| stage.getIcons().add(icon); | |||
| root.setBackground(new Background(new BackgroundFill(Paint.valueOf("#0093D1"), CornerRadii.EMPTY, Insets.EMPTY))); | |||
| stage.setScene(scene); | |||
| @@ -115,20 +115,19 @@ public class DiagnoseController { | |||
| */ | |||
| @FXML | |||
| private void initialize() { | |||
| initButtons(); | |||
| //Init diagnosis list view | |||
| diagDiagnose.itemsProperty().bind(mainController.getStammdaten().icd10CodesProperty()); | |||
| new SelectKeyComboBoxListener(diagDiagnose); | |||
| //init fields on the right | |||
| diagDiagnoseArt.setItems(FXCollections.observableArrayList(DiagArt.values())); | |||
| diagDiagnoseArzt.itemsProperty().bind(mainController.getStammdaten().mitarbeiterProperty()); | |||
| diagnoseList.itemsProperty().bind(diagnosen); | |||
| initButtons(); | |||
| fields.disableProperty().bind(state.isEqualTo(State.VIEW)); | |||
| //Init the list view on the left. | |||
| diagnoseList.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); | |||
| diagnoseList.disableProperty().bind(state.isNotEqualTo(State.VIEW)); | |||
| diagnoseList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { | |||
| @@ -139,6 +138,7 @@ public class DiagnoseController { | |||
| } | |||
| }); | |||
| //React on state changes. | |||
| state.addListener((observable, oldValue, newValue) -> { | |||
| if(newValue == State.VIEW){ | |||
| mainController.unlockFromEdit(); | |||
| @@ -168,9 +168,10 @@ public class DiagnoseController { | |||
| btnDiagEdit.disableProperty().bind(diagnoseList.getSelectionModel().selectedItemProperty().isNull()); | |||
| btnDiagEdit.managedProperty().bind(state.isEqualTo(State.VIEW)); | |||
| btnDiagEdit.visibleProperty().bind(btnDiagEdit.managedProperty()); | |||
| //btnDiagEdit.managedProperty().bind(state.isEqualTo(State.VIEW)); | |||
| //btnDiagEdit.visibleProperty().bind(btnDiagEdit.managedProperty()); | |||
| btnDiagEdit.setManaged(false); | |||
| btnDiagEdit.setVisible(false); | |||
| btnDiagSave.managedProperty().bind(state.isNotEqualTo(State.VIEW)); | |||
| btnDiagSave.visibleProperty().bind(btnDiagSave.managedProperty()); | |||
| @@ -1,8 +1,5 @@ | |||
| package de.uniluebeck.mi.projmi6.controller; | |||
| /** | |||
| * Created by 631806 on 12.11.15. | |||
| */ | |||
| import ca.uhn.hl7v2.HL7Exception; | |||
| import de.uniluebeck.mi.projmi6.db.DBHandler; | |||
| @@ -132,6 +129,9 @@ public class FallController { | |||
| @FXML | |||
| private void initialize() { | |||
| fallEinweisenderArzt.disableProperty().bind(fallSelbsteinweisung.selectedProperty()); | |||
| fallEinweisenderArzt.managedProperty().bind(fallSelbsteinweisung.selectedProperty().not()); | |||
| fallEinweisenderArzt.visibleProperty().bind(fallEinweisenderArzt.managedProperty()); | |||
| fallFallart.setItems(FXCollections.observableArrayList(FallArt.values())); | |||
| fallKasse.setItems(mainController.getStammdaten().getKassen()); | |||
| @@ -194,8 +194,6 @@ public class FallController { | |||
| */ | |||
| @FXML | |||
| private void clickedSendHl7() { | |||
| /* Natascha */ | |||
| //TODO send funny message | |||
| Patient patient = mainController.getPatientTablesController().getSelectedPatient(); | |||
| Fall fall = fallProperty.get(); | |||
| fall.setPatient(patient); | |||
| @@ -208,6 +206,41 @@ public class FallController { | |||
| } | |||
| /** | |||
| * Opens a dialog to inform the user about invalid data. | |||
| */ | |||
| private void showMessage(String title, String message) { | |||
| Alert alert = new Alert(Alert.AlertType.INFORMATION); | |||
| alert.setTitle("Ung\u00fcltige Falldaten eingegeben!"); | |||
| alert.setHeaderText(title); | |||
| alert.setContentText(message); | |||
| alert.showAndWait(); | |||
| } | |||
| /** | |||
| * Validate the data entered by the user. | |||
| */ | |||
| private boolean validateData(Fall fall){ | |||
| if(fall.getAufnahmeDatum()!= null && fall.getEntlassungsDatum() != null | |||
| && fall.getAufnahmeDatum().isAfter(fall.getEntlassungsDatum())){ | |||
| showMessage("Aufnahmedatum lieght hinter Entlassungsdatum", "Der Patient muss aufgenommen worden sein, ehe " + | |||
| "er wieder entlassen worden sein kann."); | |||
| return false; | |||
| } | |||
| if(fall.getKasse()==null || fall.getVersichertenNummer().isEmpty()){ | |||
| showMessage("Fall hat keine Kasse/Versicherungsnummer", "An den Fall m\u00fcssen Abrechnungsinfos angeh\u00e4ngt werden!"); | |||
| return false; | |||
| } | |||
| if(fall.getFallArt()==null){ | |||
| showMessage("Es ist keine Fallart ausgew\u00e4hlt!", "Bitte eine Fallart ausw\u00e4hlen!"); | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| /** | |||
| * Toggle controller state to edit | |||
| */ | |||
| public void editFall() { | |||
| @@ -255,30 +288,35 @@ public class FallController { | |||
| if (this.state.get() == State.CREATE) { | |||
| Fall fall = new Fall(); | |||
| copyFieldDataIntoFall(fall); | |||
| if(!validateData(fall)){ | |||
| return; | |||
| } | |||
| try { | |||
| int newfallid = DBHandler.setFall(fall, mainController.getCurrentMitarbeiter().getMitarbID()); | |||
| fall.setFallID(newfallid); | |||
| } catch (SQLException e) { | |||
| e.printStackTrace(); | |||
| return; | |||
| } | |||
| try { | |||
| HL7Sender.createMessageADT_A01(fall); | |||
| } catch (IOException | HL7Exception | SQLException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } else { | |||
| mainController.refreshCasesFromDb(mainController.getPatientTablesController().getSelectedPatient()); | |||
| } else { //Update / edit | |||
| copyFieldDataIntoFall(fallProperty.get()); | |||
| if(!validateData(fallProperty.get())){ | |||
| return; | |||
| } | |||
| try { | |||
| DBHandler.setFall(fallProperty.get(), mainController.getCurrentMitarbeiter().getMitarbID(), true); | |||
| } catch (SQLException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| this.state.set(State.VIEW); | |||
| mainController.refreshCasesFromDb(mainController.getPatientTablesController().getSelectedPatient()); | |||
| } | |||
| /** | |||
| @@ -25,7 +25,7 @@ public class LogController { | |||
| /** | |||
| * The superior controller | |||
| */ | |||
| final MainController mainController; | |||
| private final MainController mainController; | |||
| @FXML | |||
| private TableView<HL7LogEntry> tblLog; | |||
| @@ -60,7 +60,6 @@ public class LogController { | |||
| initColumns(); | |||
| refreshLogFromDb(); | |||
| } | |||
| /** | |||
| * Init cell value factories for the table columns | |||
| */ | |||
| @@ -110,6 +110,9 @@ public class MainController { | |||
| private int fallIdToShow = -1; | |||
| /** | |||
| * Public constructor | |||
| */ | |||
| public MainController() { | |||
| fallController = new FallController(this); | |||
| diagnoseController = new DiagnoseController(this); | |||
| @@ -307,9 +310,11 @@ public class MainController { | |||
| */ | |||
| @FXML | |||
| private void initialize() { | |||
| initCaseListView(); | |||
| //Init user data. | |||
| cmbUserChoose.itemsProperty().bind(this.getStammdaten().mitarbeiterProperty()); | |||
| cmbUserChoose.getSelectionModel().select(0); // TODO: Bessere Loesung finden. | |||
| cmbUserChoose.getSelectionModel().select(0); | |||
| //Disable the right side if no case is selected. | |||
| @@ -322,6 +327,19 @@ public class MainController { | |||
| }); | |||
| tabPaneFall.setDisable(false); | |||
| tabFallDiagnose.setDisable(true); | |||
| tabFallStationsHistorie.setDisable(true); | |||
| tabFallUntersuchungen.setDisable(true); | |||
| } | |||
| /** | |||
| * Setup the list view of the patient's cases. | |||
| */ | |||
| private void initCaseListView(){ | |||
| lvFall.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); | |||
| fallController.fallProperty().bind(lvFall.getSelectionModel().selectedItemProperty()); | |||
| @@ -383,14 +401,9 @@ public class MainController { | |||
| stationsHistorieController.setStationsHistorie(null); | |||
| diagnoseController.setDiagnosen(null); | |||
| untersuchungenController.setUntersuchungen(null); | |||
| //fallController.c | |||
| return; | |||
| } | |||
| if (fall == null) { // If no patient is selected | |||
| lvFallPlaceholder.setText("Kein Patient ausgew\u00e4hlt!"); | |||
| return; | |||
| } | |||
| loadCaseData = new Task<Void>() { | |||
| @@ -16,7 +16,7 @@ import java.io.IOException; | |||
| /** | |||
| * This controller controlls the small message icon at the bottom of the application's main window. | |||
| * Created by Johannes on 21/11/2015. | |||
| * @author Johannes | |||
| */ | |||
| public class MessageController { | |||
| /** | |||
| @@ -27,7 +27,7 @@ public class MessageController { | |||
| /** | |||
| * The list of unread messages | |||
| */ | |||
| private final SimpleListProperty<HL7Message> messages = new SimpleListProperty(FXCollections.observableArrayList()); | |||
| private final SimpleListProperty<HL7Message> messages = new SimpleListProperty<>(FXCollections.observableArrayList()); | |||
| /** | |||
| * The view that is mantained by this controller | |||
| @@ -64,8 +64,11 @@ public class MessageController { | |||
| * @see de.uniluebeck.mi.projmi6.controller.MessageListController | |||
| */ | |||
| private void showMessageList() { | |||
| //Load the window content from FXML | |||
| FXMLLoader fxmlLoader = new FXMLLoader(); | |||
| fxmlLoader.setLocation(getClass().getClassLoader().getResource("message_list.fxml")); | |||
| //Non-empty constructor of the controller class | |||
| MessageListController messageListController = new MessageListController(messages, mainController); | |||
| fxmlLoader.setControllerFactory(clazz -> messageListController); | |||
| @@ -77,6 +80,7 @@ public class MessageController { | |||
| return; | |||
| } | |||
| //Create stage and open window. | |||
| Stage stage = new Stage(); | |||
| stage.setTitle("Neue HL7-Nachrichten"); | |||
| @@ -17,7 +17,6 @@ import javafx.stage.Stage; | |||
| * @author Johannes | |||
| */ | |||
| public class MessageListController { | |||
| private final SimpleListProperty<HL7Message> messages; | |||
| private final MainController mainController; | |||
| @FXML | |||
| @@ -76,6 +76,7 @@ public class PatientTablesController { | |||
| private ObservableList<StationsUebersichtsItem> stationsUebersicht = FXCollections.observableArrayList(); | |||
| private FilteredList<StationsUebersichtsItem> stationsUebersichtsItemFilteredList = new FilteredList<StationsUebersichtsItem>(stationsUebersicht, | |||
| item -> item.getStationEntlassung() == null || item.getStationEntlassung().isAfter(LocalDate.now())); | |||
| private Task loadStationsHistorieTask = null; | |||
| private Task loadPatientTask = null; | |||
| private ObjectBinding<Patient> patientObjectBinding = null; | |||
| @@ -88,6 +89,9 @@ public class PatientTablesController { | |||
| this.mainController = mainController; | |||
| } | |||
| /** | |||
| * Getter for the TabPane that contains patient and hospital ward overview tabs. | |||
| */ | |||
| public TabPane getPatientOverviewTabPane() { | |||
| return patientOverviewTabPane; | |||
| } | |||
| @@ -288,11 +292,17 @@ public class PatientTablesController { | |||
| thread.start(); | |||
| } | |||
| /** | |||
| * EventHandler for {@link #btnStatRefresh} | |||
| */ | |||
| @FXML | |||
| private void clickedRefreshStation() { | |||
| updateStationsHistorieFromDb(); | |||
| } | |||
| /** | |||
| * Updates the hospital ward history for the currently selected hospital ward. | |||
| */ | |||
| public void updateStationsHistorieFromDb() { | |||
| if (this.loadStationsHistorieTask != null) { | |||
| loadStationsHistorieTask.cancel(); | |||
| @@ -348,15 +358,25 @@ public class PatientTablesController { | |||
| thread.start(); | |||
| } | |||
| /** | |||
| * EventHandler for the {@link #btnPatRefresh} | |||
| */ | |||
| @FXML | |||
| private void clickedRefreshPatient() { | |||
| updatePatientsFromDb(); | |||
| } | |||
| /** | |||
| * A property for the currently selected patient. Depends on the currently visible tab. | |||
| */ | |||
| public ObjectBinding<Patient> selectedPatientProperty() { | |||
| return patientObjectBinding; | |||
| } | |||
| /** | |||
| * Getter for the {@link #selectedPatientProperty()} | |||
| */ | |||
| public Patient getSelectedPatient() { | |||
| return selectedPatientProperty().get(); | |||
| } | |||
| @@ -1,4 +1,24 @@ | |||
| /** | |||
| * Controller Klassen | |||
| * This package contains the FXML Controller classes. | |||
| * | |||
| * There is the {@link de.uniluebeck.mi.projmi6.controller.MainController}, that creates instances of these nested controller classes: | |||
| * <ul> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.DiagnoseController}</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.FallController}</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.LogController}</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.PatientTablesController}</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.MessageController}</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.SettingsController}</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.StationsHistorieController}</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.UntersuchungenController}</li> | |||
| * </ul> | |||
| * The main controller has an {@link de.uniluebeck.mi.projmi6.controller.MainController#getControllerFactory()} for | |||
| * the FXMLLoader. | |||
| * | |||
| * There are also two other controller classes, that aren't directly maintained by the FXMLLoader | |||
| * <ul> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.PatientEditorController} for the edit/create patient window</li> | |||
| * <li>{@link de.uniluebeck.mi.projmi6.controller.MessageListController} for the window of unseen HL7 messages</li> | |||
| * </ul> | |||
| */ | |||
| package de.uniluebeck.mi.projmi6.controller; | |||
| @@ -1,4 +1,5 @@ | |||
| /** | |||
| * Model Klassen. | |||
| * Model classes. | |||
| * In this project we use a relative flat object structure, that correlate directly with the db table columns. | |||
| */ | |||
| package de.uniluebeck.mi.projmi6.model; | |||
| @@ -37,6 +37,31 @@ public class DateTimePicker extends HBox { | |||
| */ | |||
| public DateTimePicker() { | |||
| this.setAlignment(Pos.CENTER_LEFT); | |||
| HBox timePicker = initTimePicker(); | |||
| //Set Now-Button action | |||
| btnNow.setOnAction(event -> setToCurrentDateTime()); | |||
| //Make it large enough to read the text | |||
| btnNow.setMinWidth(50); | |||
| btnNow.getStyleClass().add("now-button"); | |||
| //Jetzt-Button nimmt keine Platz ein wenn View deaktiviert: | |||
| btnNow.managedProperty().bind(this.disabledProperty().not()); | |||
| //Add the subcomponents to the view. | |||
| this.getChildren().addAll(datePicker, timePicker, btnNow); | |||
| this.setSpacing(5); | |||
| } | |||
| /** | |||
| * Inits the timepicker, which is composed from an HBox and two textfields | |||
| */ | |||
| private HBox initTimePicker(){ | |||
| hourText.setOnKeyReleased(event -> { | |||
| if (hourText.getCaretPosition() >= 2) { | |||
| int hour = Integer.parseInt(hourText.getText()); | |||
| @@ -48,11 +73,13 @@ public class DateTimePicker extends HBox { | |||
| hourText.setAlignment(Pos.CENTER_RIGHT); | |||
| hourText.setBackground(null); | |||
| hourText.setPadding(Insets.EMPTY); | |||
| hourText.setPromptText("HH"); | |||
| hourText.promptTextProperty().bind(Bindings.createStringBinding( | |||
| ()->this.isDisabled()?"":"HH", this.disabledProperty())); | |||
| HBox.setHgrow(hourText, Priority.ALWAYS); | |||
| minuteText.setBackground(null); | |||
| minuteText.setPadding(Insets.EMPTY); | |||
| minuteText.setPromptText("MM"); | |||
| minuteText.promptTextProperty().bind(Bindings.createStringBinding( | |||
| ()->this.isDisabled()?"":"MM", this.disabledProperty())); | |||
| HBox.setHgrow(minuteText, Priority.ALWAYS); | |||
| hourText.effectProperty().bind(Bindings.<Effect>createObjectBinding(() -> { | |||
| @@ -81,7 +108,12 @@ public class DateTimePicker extends HBox { | |||
| return invalidValue; | |||
| }, minuteText.textProperty())); | |||
| Label colon = new Label(":"); | |||
| colon.visibleProperty().bind(Bindings.createBooleanBinding(()->{ | |||
| if(this.isDisabled() && (minuteText.getText()==null || minuteText.getText().isEmpty())){ | |||
| return false; | |||
| } | |||
| return true; | |||
| },this.disabledProperty(), minuteText.textProperty())); | |||
| HBox timePicker = new HBox(hourText, colon, minuteText); | |||
| timePicker.maxHeightProperty().bind(datePicker.heightProperty()); | |||
| timePicker.getStyleClass().add("time-picker"); | |||
| @@ -89,20 +121,7 @@ public class DateTimePicker extends HBox { | |||
| timePicker.setMaxWidth(80); | |||
| timePicker.getStyleClass().add("button"); | |||
| //Set Now-Button action | |||
| btnNow.setOnAction(event -> setToCurrentDateTime()); | |||
| //Make it large enough to read the text | |||
| btnNow.setMinWidth(50); | |||
| btnNow.getStyleClass().add("now-button"); | |||
| btnNow.managedProperty().bind(this.disabledProperty().not()); | |||
| //Add the subcomponents to the view. | |||
| this.getChildren().addAll(datePicker, timePicker, btnNow); | |||
| this.setSpacing(5); | |||
| return timePicker; | |||
| } | |||
| /** | |||
| @@ -1,4 +1,4 @@ | |||
| /** | |||
| * View Klassen. | |||
| * The custom view classes. | |||
| */ | |||
| package de.uniluebeck.mi.projmi6.view; | |||
| @@ -61,3 +61,9 @@ DateTimePicker .text-field { | |||
| visibility: hidden; | |||
| } | |||
| .fields:disabled .check-box { | |||
| -fx-opacity: 0; | |||
| } | |||
| .fiels:disabled .check-box:selected{ | |||
| -fx-opacity: 1; | |||
| } | |||