diff options
60 files changed, 4189 insertions, 71 deletions
@@ -117,3 +117,6 @@ dist/  nbdist/  nbactions.xml  .nb-gradle/ + +# db +sepm.*.db @@ -10,52 +10,48 @@ Projektbeschreibung  ## Projektteam -*   **Rolle:**          Teamkoordinator/Testleiter Stv.\ -    **Name:**           Weick, Martin\ +*   **Rolle:**          Teamkoordinator/Testleiter Stv.   +    **Name:**           Weick, Martin        **Matrikelnummer:** e01627760 -*   **Rolle:**          Testleiter/Teamkoordinator Stv.\ -    **Name:**           Rogetzer, Dominic\ +*   **Rolle:**          Testleiter/Teamkoordinator Stv.   +    **Name:**           Rogetzer, Dominic        **Matrikelnummer:** e01627756 -*   **Rolle:**          Technischer Architekt/Git Koordinator\ -    **Name:**           Gschwantner, Thomas\ +*   **Rolle:**          Technischer Architekt/Git Koordinator   +    **Name:**           Gschwantner, Thomas        **Matrikelnummer:** e01625751 -*   **Rolle:**          UX-Designer/Technischer Architekt Stv.\ -    **Name:**           Weninger, Andreas\ +*   **Rolle:**          UX-Designer/Technischer Architekt Stv.   +    **Name:**           Weninger, Andreas        **Matrikelnummer:** e01526989 -*   **Rolle:**          Dokumentationsbeauftragter/Kommunikationsbeauftragter Stv.\ -    **Name:**           Kehrer, Felix\ +*   **Rolle:**          Dokumentationsbeauftragter/Kommunikationsbeauftragter Stv.   +    **Name:**           Kehrer, Felix        **Matrikelnummer:** e01526278 -*   **Rolle:**          Kommunikationsbeauftragte/Dokumentationsbeauftragte Stv./UX-Designer Stv.\ -    **Name:**           Pundy, Viktoria\ +*   **Rolle:**          Kommunikationsbeauftragte/Dokumentationsbeauftragte Stv./UX-Designer Stv.   +    **Name:**           Pundy, Viktoria        **Matrikelnummer:** e01633403  ## Kurzanleitung -*   Das Programm kann mit dem Befehl:\ -    ```mvnw compile```\ +*   Das Programm kann mit dem Befehl:   +    ```mvnw compile```        gebaut werden können. -*   Das Programm kann mit dem Befehl:\ -    ```mvnw test```\ +*   Das Programm kann mit dem Befehl:   +    ```mvnw test```        getestet werden können. -*   Das Programm kann mit dem Befehl:\ -    ```mvnw exec:java```\ +*   Das Programm kann mit dem Befehl:   +    ```mvnw exec:java```        ausgeführt werden. -*   Das Programm kann mit dem Befehl:\ -    ```mvnw package```\ +*   Das Programm kann mit dem Befehl:   +    ```mvnw package```        als ausführbares jar-File packetiert werden. -*   Das Programm kann mit dem Befehl:\ -    ```mvnw package```\ -    als ausführbares jar-File packetiert werden. - -*   Das Programm kann mit dem Befehl:\ -    ```mvnw clean verify && java -jar ./target/*.jar```\ +*   Das Programm kann mit dem Befehl:   +    ```mvnw clean verify && java -jar ./target/*.jar```        als ausführbares jar-File packetiert, verifiziert und ausgeführt werden. @@ -25,6 +25,8 @@          <junit.version>4.12</junit.version>          <mockito.version>2.18.0</mockito.version>          <google-java-format.version>1.5</google-java-format.version> +        <testfx.version>4.0.13-alpha</testfx.version> +        <dbunit.version>2.5.1</dbunit.version>          <!-- plugins -->          <maven-surefire-plugin.version>2.21.0</maven-surefire-plugin.version>          <maven-shade-plugin.version>3.1.1</maven-shade-plugin.version> @@ -65,13 +67,13 @@            <version>${auto-value.version}</version>            <scope>provided</scope>          </dependency> -        <!-- runtime dependencies -->          <dependency>              <groupId>com.h2database</groupId>              <artifactId>h2</artifactId>              <version>${h2.version}</version> -            <scope>runtime</scope> +            <scope>compile</scope>          </dependency> +        <!-- runtime dependencies -->          <dependency>              <groupId>ch.qos.logback</groupId>              <artifactId>logback-classic</artifactId> @@ -102,6 +104,23 @@              <artifactId>google-java-format</artifactId>              <version>${google-java-format.version}</version>          </dependency> +        <dependency> +            <groupId>org.testfx</groupId> +            <artifactId>testfx-core</artifactId> +            <version>${testfx.version}</version> +            <scope>test</scope> +        </dependency> +        <dependency> +            <groupId>org.testfx</groupId> +            <artifactId>testfx-junit</artifactId> +            <version>${testfx.version}</version> +            <scope>test</scope> +        </dependency> +        <dependency> +            <groupId>org.dbunit</groupId> +            <artifactId>dbunit</artifactId> +            <version>${dbunit.version}</version> +        </dependency>      </dependencies>      <build> @@ -112,8 +131,8 @@                  <version>${maven-surefire-plugin.version}</version>                  <configuration>                      <!-- enable parallel test runs --> -                    <parallel>methods</parallel> -                    <threadCount>10</threadCount> +                    <!--parallel>methods</parallel--> +                    <!--threadCount>10</threadCount-->                      <!-- discover all *.java files -->                      <includes>                          <include>*.java</include> diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/application/MainApplication.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/application/MainApplication.java new file mode 100644 index 0000000..01c04d3 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/application/MainApplication.java @@ -0,0 +1,52 @@ +package at.ac.tuwien.sepm.assignment.groupphase.application; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.userInterface.CreateOperationController; +import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; + +@Component +@ComponentScan("at.ac.tuwien.sepm.assignment.groupphase") +public class MainApplication extends Application { + +    private static AnnotationConfigApplicationContext configApplicationContext; + +    public static void main(String[] args) { +        Application.launch(MainApplication.class, args); +    } + +    @Override +    public void start(Stage primaryStage) throws Exception { +        primaryStage.setTitle("Einsatz erstellen"); +        primaryStage.centerOnScreen(); +        primaryStage.setOnCloseRequest(event -> Platform.exit()); + +        configApplicationContext = new AnnotationConfigApplicationContext(MainApplication.class); +        final var fxmlLoader = configApplicationContext.getBean(SpringFXMLLoader.class); +        primaryStage.setScene( +                new Scene( +                        (Parent) +                                fxmlLoader.load( +                                        getClass() +                                                .getResourceAsStream( +                                                        "/fxml/CreateOperationController.fxml")))); + +        CreateOperationController controller = +                configApplicationContext.getBean(CreateOperationController.class); +        controller.updateList(); +        primaryStage.show(); +        primaryStage.toFront(); +    } + +    @Override +    public void stop() throws Exception { +        super.stop(); +        configApplicationContext.close(); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/CreateCarController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/CreateCarController.java new file mode 100644 index 0000000..b6693d0 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/CreateCarController.java @@ -0,0 +1,116 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.controller; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.ConstructionType; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.Status; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.VehicleType; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleService; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.lang.invoke.MethodHandles; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javafx.collections.FXCollections; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ChoiceBox; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; + +@Controller +public class CreateCarController { + +    @FXML private ChoiceBox<String> cmb_Ctyp; +    @FXML private ChoiceBox<String> cmb_typ; +    @FXML private Button btn_cancel; +    @FXML private CheckBox cbx_NEF; + +    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); +    private final VehicleService vehicleService; + +    public CreateCarController(VehicleService vehicleService) { +        this.vehicleService = vehicleService; +    } + +    @FXML +    public void initialize() { +        cmb_Ctyp.setItems( +                FXCollections.observableArrayList( +                        Stream.of( +                                        ConstructionType.NORMAL, +                                        ConstructionType.MITTELHOCHDACH, +                                        ConstructionType.HOCHDACH) +                                .map(Enum::toString) +                                .collect(Collectors.toList()))); +        cmb_Ctyp.setValue(ConstructionType.NORMAL.toString()); +        cmb_typ.setItems( +                FXCollections.observableArrayList( +                        Stream.of( +                                        VehicleType.BKTW, +                                        VehicleType.KTW_B, +                                        VehicleType.KTW, +                                        VehicleType.RTW, +                                        VehicleType.NEF, +                                        VehicleType.NAH) +                                .map(Enum::toString) +                                .collect(Collectors.toList()))); +        cmb_typ.setValue(VehicleType.BKTW.toString()); +    } + +    @FXML +    public void onCancelClicked() { +        ((Stage) btn_cancel.getScene().getWindow()).close(); +    } + +    @FXML +    public void createCar(ActionEvent actionEvent) { +        Vehicle vehicle = +                Vehicle.builder() +                        .constructionType(parseConstructionType()) +                        .type(parseType()) +                        .name("") +                        .status(Status.ABGEMELDET) +                        .hasNef(cbx_NEF.isSelected()) +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException e) { +            LOG.error("Invalid Vehicle: {}", e); +            createComplete(AlertType.ERROR, "Ungültige Eingabe", e.getMessage()); +            return; +        } catch (ServiceException e) { +            LOG.error("Exception: {}", e); +            createComplete(AlertType.ERROR, "Fehler", e.getMessage()); +            return; +        } +        createComplete( +                AlertType.CONFIRMATION, "Speichern Erfolgreich", "Auto wurde erfolgreich angelegt"); +    } + +    private ConstructionType parseConstructionType() { +        if (cmb_Ctyp.getSelectionModel().getSelectedItem() == null) { +            return ConstructionType.NORMAL; +        } +        return ConstructionType.valueOf(cmb_Ctyp.getSelectionModel().getSelectedItem().toString()); +    } + +    private VehicleType parseType() { +        if (cmb_typ.getSelectionModel().getSelectedItem() == null) { +            return VehicleType.BKTW; +        } +        return VehicleType.valueOf(cmb_typ.getSelectionModel().getSelectedItem().toString()); +    } + +    private void createComplete(AlertType alertType, String headerText, String contentText) { +        Alert alert = new Alert(alertType, contentText, ButtonType.OK); +        alert.setHeaderText(headerText); +        alert.showAndWait(); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/CreateNewEmployeeController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/CreateNewEmployeeController.java new file mode 100644 index 0000000..d81f6d7 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/CreateNewEmployeeController.java @@ -0,0 +1,117 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.controller; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.EmployeeService; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.lang.invoke.MethodHandles; +import java.time.LocalDate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javafx.collections.FXCollections; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.TextField; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; + +@Controller +public class CreateNewEmployeeController { + +    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); +    private final EmployeeService employeeService; + +    @FXML private CheckBox inputIsDriver; +    @FXML private CheckBox inputIsPilot; +    @FXML private Hyperlink btnCancel; +    @FXML private Button btnCreate; +    @FXML private TextField inputName; +    @FXML private ChoiceBox<String> inputQualification; + +    public CreateNewEmployeeController(EmployeeService employeeService) { +        this.employeeService = employeeService; +    } + +    @FXML +    public void initialize() { +        inputQualification.setItems( +                FXCollections.observableArrayList( +                        Stream.of( +                                        EducationLevel.RS, +                                        EducationLevel.NFS, +                                        EducationLevel.NKV, +                                        EducationLevel.NKA, +                                        EducationLevel.NKI, +                                        EducationLevel.NA) +                                .map(Enum::toString) +                                .collect(Collectors.toList()))); + +        inputQualification.setValue(EducationLevel.RS.toString()); +    } + +    @FXML +    public void onCancelClicked() { +        ((Stage) inputQualification.getScene().getWindow()).close(); +    } + +    @FXML +    public void onCreateClicked() { + +        Employee employee = +                Employee.builder() +                        .name(inputName.getText()) +                        .educationLevel(parseEducationLevel()) +                        .birthday(LocalDate.MIN) // TODO: change UI to include birthday field +                        .isDriver(inputIsDriver.isSelected()) +                        .isPilot(inputIsPilot.isSelected()) +                        .build(); + +        try { +            employeeService.add(employee); +        } catch (InvalidEmployeeException e) { +            LOG.error("Invalid Employee: {}", e); + +            showModalDialogWithOkButton( +                    AlertType.ERROR, +                    "Ungültige Eingabe", +                    "Mindestens eines der Eingabefelder haben einen ungültigen Wert!"); +            return; +        } catch (ServiceException e) { +            LOG.error("Employee could not be saved: {}", e); + +            showModalDialogWithOkButton( +                    AlertType.ERROR, +                    "Speicherfehler", +                    "Der Eintrag konnte nicht gespeichert werden. Bitte versuchen Sie es erneut."); +            return; +        } + +        showModalDialogWithOkButton( +                AlertType.INFORMATION, +                "Erfolgreich angelegt", +                "Mitarbeiter wurde erfolgreich angelegt und gespeichert!"); +    } + +    private void showModalDialogWithOkButton( +            AlertType alertType, String headerText, String contentText) { +        Alert alert = new Alert(alertType, contentText, ButtonType.OK); +        alert.setHeaderText(headerText); +        alert.showAndWait(); +    } + +    private EducationLevel parseEducationLevel() { +        if (inputQualification.getSelectionModel().getSelectedItem() == null) { +            return EducationLevel.RS; +        } +        return EducationLevel.valueOf(inputQualification.getSelectionModel().getSelectedItem()); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/RegistrationWindowController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/RegistrationWindowController.java new file mode 100644 index 0000000..bf413bb --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/RegistrationWindowController.java @@ -0,0 +1,204 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.controller; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registration; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.Status; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.EmployeeService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.RegistrationService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleService; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Label; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; + +@Controller +public class RegistrationWindowController { + +    private static final Logger LOG = LoggerFactory.getLogger(RegistrationWindowController.class); + +    private EmployeeService employeeService; + +    private VehicleService vehicleService; + +    private RegistrationService registrationService; + +    @Autowired +    public void setEmployeeService(EmployeeService employeeService) { +        this.employeeService = employeeService; +    } + +    @Autowired +    public void setVehicleService(VehicleService vehicleService) { +        this.vehicleService = vehicleService; +    } + +    @Autowired +    public void setRegistrationService(RegistrationService registrationService) { +        this.registrationService = registrationService; +    } + +    @FXML public ChoiceBox<Integer> cbStart; +    @FXML public ChoiceBox<Integer> cbEnd; +    @FXML public Label lVehicles; +    @FXML public Label lEmployees; +    @FXML public TextField tfVehicleSearch; +    @FXML public TextField tfEmployeeSearch; +    @FXML public TableView<Vehicle> tvVehicles; +    @FXML public TableView<Employee> tvEmployees; +    @FXML public TableColumn<Vehicle, String> tcVehicles; +    @FXML public TableColumn<Employee, String> tcEmployees; + +    private Vehicle chosenVehicle; +    private List<Employee> chosenEmployees = new LinkedList<>(); + +    @FXML +    public void initialize() { +        // will have to be replaced for FlowPane +        try { +            List<Vehicle> vehicles = vehicleService.list(EnumSet.of(Status.ABGEMELDET)); +            tcVehicles.setCellValueFactory(x -> new SimpleStringProperty(x.getValue().name())); +            tvVehicles.setItems(FXCollections.observableArrayList(vehicles)); +        } catch (ServiceException e) { +            LOG.warn( +                    "Caught ServiceException while getting vehicles. Showing it to user. Error message: {}", +                    e.getMessage()); +            Alert alert = new Alert(AlertType.ERROR); +            alert.setTitle("Fahrzeuge - Fehler!"); +            alert.setHeaderText("Beim Auflisten der Fahrzeug ist ein Fehler aufgetreten."); +            alert.setContentText(e.getMessage()); +            alert.show(); +        } +        try { +            List<Employee> employees = employeeService.list(); +            tcEmployees.setCellValueFactory(x -> new SimpleStringProperty(x.getValue().name())); +            tvEmployees.setItems(FXCollections.observableArrayList(employees)); +        } catch (ServiceException e) { +            LOG.warn( +                    "Caught ServiceException while getting employees. Showing it to user. Error message: {}", +                    e.getMessage()); +            Alert alert = new Alert(AlertType.ERROR); +            alert.setTitle("Personal - Fehler!"); +            alert.setHeaderText("Beim Auflisten des Personals ist ein Fehler aufgetreten."); +            alert.setContentText(e.getMessage()); +            alert.show(); +        } +        tvVehicles.setOnMousePressed( +                mouseEvent -> { +                    if (mouseEvent.isPrimaryButtonDown() && mouseEvent.getClickCount() == 2) { +                        chosenVehicle = tvVehicles.getSelectionModel().getSelectedItem(); +                        if (chosenVehicle == null) { +                            return; +                        } +                        lVehicles.setText(chosenVehicle.name()); +                    } +                }); +        tvEmployees.setOnMousePressed( +                mouseEvent -> { +                    if (mouseEvent.isPrimaryButtonDown() && mouseEvent.getClickCount() == 2) { +                        chosenEmployees.add(tvEmployees.getSelectionModel().getSelectedItem()); +                        if (chosenEmployees == null) { +                            return; +                        } +                        StringBuilder text = new StringBuilder(); +                        for (Employee employee : chosenEmployees) { +                            text.append(employee.name()).append("\n"); +                        } +                        lEmployees.setText(text.toString()); +                    } +                }); +        ObservableList<Integer> hours = +                FXCollections.observableArrayList( +                        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +                        21, 22, 23); +        cbStart.setItems(hours); +        cbStart.setValue(0); +        cbEnd.setItems(hours); +        cbEnd.setValue(12); +    } + +    public void cancel() { +        LOG.debug("Cancel Button clicked"); +        ((Stage) lVehicles.getScene().getWindow()).close(); +    } + +    public void create() { +        LOG.debug("Create Button clicked"); + +        List<Registration> registrations = new LinkedList<>(); + +        for (Employee employee : chosenEmployees) { +            registrations.add( +                    Registration.builder() +                            .id(chosenVehicle.id()) +                            .employee(employee) +                            .start( +                                    LocalDateTime.of( +                                                    LocalDate.now(), +                                                    LocalTime.of(cbStart.getValue(), 0)) +                                            .toInstant(OffsetDateTime.now().getOffset())) +                            .end( +                                    LocalDateTime.of( +                                                    LocalDate.now(), +                                                    LocalTime.of(cbEnd.getValue(), 0)) +                                            .toInstant(OffsetDateTime.now().getOffset())) +                            .build()); +        } +        try { +            registrationService.add(chosenVehicle.id(), registrations); +            ((Stage) lVehicles.getScene().getWindow()).close(); +        } catch (InvalidVehicleException e) { +            // NOT THROWN ANYWHERE RIGHT NOW +            LOG.info( +                    "Caught InvalidVehicleException. Showing it to user. Error message: {}", +                    e.getClass().toString(), +                    e.getMessage()); +            Alert alert = new Alert(AlertType.WARNING); +            alert.setTitle("Ungültiges Fahrzeug"); +            alert.setHeaderText("Das spezifizierte Fahrzeug ist nicht gültig."); +            alert.setContentText(e.getMessage()); +            alert.show(); +        } catch (ServiceException e) { +            LOG.warn( +                    "Caught ServiceException while getting vehicles. Showing it to user. Error message: {}", +                    e.getMessage()); +            Alert alert = new Alert(AlertType.ERROR); +            alert.setTitle("Anmeldung - Fehler!"); +            alert.setHeaderText("Beim Erstellen der Anmeldung ist ein Fehler aufgetreten."); +            alert.setContentText(e.getMessage()); +            alert.show(); +        } catch (InvalidRegistrationException e) { +            LOG.info( +                    "Caught InvalidRegistrationException. Showing it to user. Error message: {}", +                    e.getMessage()); +            Alert alert = new Alert(AlertType.WARNING); +            alert.setTitle("Ungültige Eingabe"); +            alert.setHeaderText( +                    "Die gewählte Kombination von Fahrzeug und Personal ist nicht gültig!"); +            alert.setContentText(e.getMessage()); +            alert.show(); +        } +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/DBOperationDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/DBOperationDAO.java new file mode 100644 index 0000000..672424a --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/DBOperationDAO.java @@ -0,0 +1,161 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation.Status; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.EnumSet; +import java.util.List; + +public class DBOperationDAO implements OperationDAO { + +    private JDBCConnectionManager jdbcConnectionManager; + +    public DBOperationDAO(JDBCConnectionManager j) { +        jdbcConnectionManager = j; +    } + +    @Override +    public long add(Operation operation) throws PersistenceException { +        if (operation == null) { +            throw new PersistenceException("Das der Datenbank übergebene Objekt ist fehlerhaft!"); +        } +        PreparedStatement pstmt = null; +        try { +            pstmt = +                    jdbcConnectionManager +                            .getConnection() +                            .prepareStatement( +                                    "INSERT INTO operation(opCode, severity, " +                                            + "created, destination, additionalInfo, status) values (?,?,?,?,?,?)", +                                    java.sql.Statement.RETURN_GENERATED_KEYS); + +            if (operation.opCode() == null) { +                throw new PersistenceException("Code darf nicht null sein!"); +            } else if (operation.opCode().length() > 20) +                throw new PersistenceException( +                        "Länge des OP-Codes überschreitet erlaubte Länge von 100 Zeichen!"); +            else pstmt.setString(1, operation.opCode()); +            /*switch (operation.severity()) { +                case A: +                    pstmt.setInt(2, 0); +                    break; +                case B: +                    pstmt.setInt(2, 1); +                    break; +                case C: +                    pstmt.setInt(2, 2); +                    break; +                case D: +                    pstmt.setInt(2, 3); +                    break; +                case E: +                    pstmt.setInt(2, 4); +                    break; +                case O: +                    pstmt.setInt(2, 5); +                    break; +                default: +                    throw new PersistenceException( +                            "Schwere des Einsatzes konnte nicht validiert werden!"); +            }*/ +            pstmt.setString(2, operation.severity().name()); +            if (operation.created() != null) { +                pstmt.setTimestamp(3, Timestamp.from(operation.created())); +            } else { +                throw new PersistenceException("Zeitpunkt der Erstellung darf nicht null sein!"); +            } + +            if (operation.destination() == null) { +                throw new PersistenceException("Einsatzort darf nicht null sein!"); +            } else if (operation.destination().length() > 100) { +                throw new PersistenceException( +                        "Länge der Adresse überschreitet erlaubte Länge von 100 Zeichen!"); +            } else { +                pstmt.setString(4, operation.destination()); +            } +            if (operation.additionalInfo().length() > 100) +                throw new PersistenceException( +                        "Länge der zusätzlichen Information überschreitet erlaubte Länge von 100 Zeichen!"); +            else pstmt.setString(5, operation.additionalInfo()); +            if (operation.status() == null) { +                throw new PersistenceException("Status darf nicht null sein!"); +            } else if (operation.status().toString().length() > 100) { +                throw new PersistenceException( +                        "Länge des Status überschreitet erlaubte Länge von 100 Zeichen!"); +            } else { +                pstmt.setString(6, operation.status().toString()); +            } +            pstmt.executeUpdate(); +            ResultSet rs = pstmt.getGeneratedKeys(); +            if (rs.next()) return rs.getInt(1); +            else throw new PersistenceException("Einsatz konnte nicht gespeichert werden"); +        } catch (SQLException e) { +            throw new PersistenceException(e); +        } finally { +            if (pstmt != null) { +                try { +                    pstmt.close(); +                } catch (SQLException e) { +                    throw new PersistenceException( +                            "Verbindung zur Datenbank konnte nicht geschlossen werden!", e); +                } +            } +        } +    } + +    @Override +    public void update(Operation operation) throws ElementNotFoundException, PersistenceException {} + +    @Override +    public Operation get(long operationId) throws ElementNotFoundException, PersistenceException { +        return null; +    } + +    @Override +    public List<Operation> list(EnumSet<Status> statuses) throws PersistenceException { +        return null; +    } + +    @Override +    public int connectVehicleToOperation(long vehicleID, long operationID) +            throws PersistenceException { +        PreparedStatement pstmt = null; +        try { +            pstmt = +                    jdbcConnectionManager +                            .getConnection() +                            .prepareStatement( +                                    "insert into VehicleOperation(vehicleId, operationId)" +                                            + "values (?,?)"); +            pstmt.setLong(1, vehicleID); +            pstmt.setLong(2, operationID); +            pstmt.executeUpdate(); + +            /* +            ResultSet rs = pstmt.getGeneratedKeys(); +            if (rs.next()) return rs.getInt(1); +            else +                throw new PersistenceException( +                        "Fahrzeug für die Operation konnte nicht abgespeichert werden!");*/ +        } catch (SQLException e) { +            throw new PersistenceException("Die Werte konnten nicht gespeichert werden!"); +        } finally { +            if (pstmt != null) { +                try { +                    pstmt.close(); +                } catch (SQLException e) { +                    throw new PersistenceException( +                            "Verbindung zur Datenbank konnte nicht geschlossen werden!", e); +                } +            } +        } + +        return 1; +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/EmployeeDatabaseDao.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/EmployeeDatabaseDao.java new file mode 100644 index 0000000..3e4ba12 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/EmployeeDatabaseDao.java @@ -0,0 +1,124 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager; +import java.lang.invoke.MethodHandles; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; + +@Repository +public class EmployeeDatabaseDao implements EmployeeDAO { + +    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); +    private static final String INSERT_EMPLOYEE_VERSION = +            "INSERT INTO EmployeeVersion(name, birthday, educationLevel, isDriver, isPilot) " +                    + "VALUES(?, ?, ?, ?, ?)"; +    private static final String INSERT_EMPLOYEE = "INSERT INTO Employee(version) VALUES(?)"; +    private static final String LIST_EMPLOYEE = +            "SELECT emp.id, v.name, v.birthday, v.educationLevel, v.isDriver, v.isPilot " +                    + "FROM employee emp " +                    + "JOIN EmployeeVersion v ON v.id = emp.version"; + +    private final PreparedStatement insertEmployeeVersion, insertEmployee, listEmployee; + +    public EmployeeDatabaseDao(JDBCConnectionManager connectionManager) +            throws PersistenceException { + +        try { + +            final var connection = connectionManager.getConnection(); +            insertEmployeeVersion = +                    connection.prepareStatement( +                            INSERT_EMPLOYEE_VERSION, Statement.RETURN_GENERATED_KEYS); +            insertEmployee = +                    connection.prepareStatement(INSERT_EMPLOYEE, Statement.RETURN_GENERATED_KEYS); + +            listEmployee = connection.prepareStatement(LIST_EMPLOYEE); + +        } catch (SQLException e) { +            throw new PersistenceException(e); +        } +    } + +    @Override +    public long add(Employee employee) throws PersistenceException { + +        // Assumption: the given employee is already validated (from service) +        try { +            insertEmployeeVersion.setString(1, employee.name()); +            insertEmployeeVersion.setTimestamp( +                    2, Timestamp.valueOf(employee.birthday().atStartOfDay())); +            insertEmployeeVersion.setString(3, employee.educationLevel().toString()); +            insertEmployeeVersion.setBoolean(4, employee.isDriver()); +            insertEmployeeVersion.setBoolean(5, employee.isPilot()); +            insertEmployeeVersion.executeUpdate(); +            ResultSet resultSetEmployeeVersion = insertEmployeeVersion.getGeneratedKeys(); +            if (resultSetEmployeeVersion.next()) { +                long versionId = resultSetEmployeeVersion.getLong(1); + +                insertEmployee.setLong(1, versionId); +                insertEmployee.executeUpdate(); + +                ResultSet resultSetEmployee = insertEmployee.getGeneratedKeys(); +                if (resultSetEmployee.next()) { +                    return resultSetEmployee.getLong(1); +                } +            } + +            throw new PersistenceException("Employee was not created"); + +        } catch (SQLException e) { +            throw new PersistenceException(e); +        } +    } + +    @Override +    public void update(Employee employee) throws ElementNotFoundException, PersistenceException { +        throw new UnsupportedOperationException(); +    } + +    @Override +    public List<Employee> list() throws PersistenceException { + +        try { +            ResultSet rs = listEmployee.executeQuery(); + +            List<Employee> employees = new ArrayList<>(); +            while (rs.next()) { + +                Employee employee = +                        Employee.builder() +                                .id(rs.getLong(1)) +                                .name(rs.getString(2)) +                                .birthday(rs.getTimestamp(3).toLocalDateTime().toLocalDate()) +                                .educationLevel(EducationLevel.valueOf(rs.getString(4))) +                                .isDriver(rs.getBoolean(5)) +                                .isPilot(rs.getBoolean(6)) +                                .build(); + +                employees.add(employee); +            } + +            return employees; + +        } catch (SQLException e) { +            throw new PersistenceException(e); +        } +    } + +    @Override +    public void remove(long id) throws ElementNotFoundException, PersistenceException { +        throw new UnsupportedOperationException(); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/OperationDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/OperationDAO.java index 7f28005..dd1a189 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/OperationDAO.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/OperationDAO.java @@ -45,4 +45,6 @@ public interface OperationDAO {       * @throws PersistenceException if loading the stored operations failed       */      List<Operation> list(EnumSet<Status> statuses) throws PersistenceException; + +    int connectVehicleToOperation(long vehicleID, long operationID) throws PersistenceException;  } diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDAO.java index f2c461a..ba8f909 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDAO.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDAO.java @@ -3,6 +3,7 @@ package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao;  import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registration;  import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException;  import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import java.util.List;  public interface RegistrationDAO { @@ -10,11 +11,11 @@ public interface RegistrationDAO {       * Persist the given registration.       *       * @param vehicleId the id of the target vehicle -     * @param registration that should be stored -     * @return the id that was assigned +     * @param registrations that should be stored +     * @return a list of the ids that were assigned       * @throws PersistenceException if the registration could not be persisted       */ -    long add(long vehicleId, Registration registration) throws PersistenceException; +    List<Long> add(long vehicleId, List<Registration> registrations) throws PersistenceException;      /**       * Make registration with the given id inactive. diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDatabaseDAO.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDatabaseDAO.java new file mode 100644 index 0000000..8fbcd18 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDatabaseDAO.java @@ -0,0 +1,106 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registration; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.LinkedList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +@Repository +public class RegistrationDatabaseDAO implements RegistrationDAO { + +    private static final Logger LOG = LoggerFactory.getLogger(RegistrationDatabaseDAO.class); + +    private static final String ADD_REGISTRATION = +            "INSERT INTO Registration (vehicleId, employeeId, start, end, active) VALUES (?,?,?,?,?);"; +    private static final String UPDATE_VEHICLE = +            "UPDATE Vehicle SET status = 'FREI_WACHE' WHERE id = ?;"; + +    private PreparedStatement addRegistration; +    private PreparedStatement updateVehicle; + +    private Connection connection; + +    @Autowired +    public RegistrationDatabaseDAO(JDBCConnectionManager connectionManager) +            throws PersistenceException { +        try { +            connection = connectionManager.getConnection(); +            addRegistration = +                    connection.prepareStatement(ADD_REGISTRATION, Statement.RETURN_GENERATED_KEYS); +            updateVehicle = connection.prepareStatement(UPDATE_VEHICLE); +        } catch (SQLException e) { +            LOG.error("Could not get connection or preparation of statement failed"); +            throw new PersistenceException(e); +        } +    } + +    @Override +    public List<Long> add(long vehicleId, List<Registration> registrations) +            throws PersistenceException { +        List<Long> returnValues = new LinkedList<>(); +        try { +            connection.setAutoCommit(false); +            for (Registration registration : registrations) { +                addRegistration.setLong(1, vehicleId); +                addRegistration.setLong(2, registration.employee().id()); +                addRegistration.setTimestamp(3, Timestamp.from(registration.start())); +                addRegistration.setObject(4, registration.end()); +                addRegistration.setBoolean( +                        5, true); // ASSUMPTION: Registration gets created as active +                addRegistration.executeUpdate(); +                try (ResultSet rs = addRegistration.getGeneratedKeys()) { +                    if (rs.next()) { +                        returnValues.add(rs.getLong(1)); +                    } else { +                        LOG.error("No ResultSet was created while adding registration"); +                        throw new PersistenceException( +                                "Anmeldung konnte nicht gespeichert werden."); +                    } +                } +            } + +            updateVehicle.setLong(1, vehicleId); +            updateVehicle.executeUpdate(); + +            connection.commit(); +            return returnValues; +        } catch (SQLException e) { +            LOG.error( +                    "An SQLException occurred while trying to save registrations to database. " +                            + "Attempting a rollback. Error message: {}", +                    e.getMessage()); +            try { +                connection.rollback(); +            } catch (SQLException e1) { +                LOG.error("Rollback failed :("); +            } +            throw new PersistenceException(e); +        } finally { +            try { +                connection.setAutoCommit(true); +            } catch (SQLException e) { +                LOG.error( +                        "Setting back AutoCommit to false failed! Error message: {}", +                        e.getMessage()); +                // SonarLint insists on me not throwing anything here... +            } +        } +    } + +    @Override +    public void remove(long id) throws ElementNotFoundException, PersistenceException { +        throw new UnsupportedOperationException(); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/VehicleDatabaseDao.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/VehicleDatabaseDao.java new file mode 100644 index 0000000..ca1d45c --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/VehicleDatabaseDao.java @@ -0,0 +1,147 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.ConstructionType; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.Status; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.VehicleType; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ElementNotFoundException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; +import org.springframework.stereotype.Repository; + +@Repository +public class VehicleDatabaseDao implements VehicleDAO { + +    private final JDBCConnectionManager jdbcConnectionManager; + +    public VehicleDatabaseDao(JDBCConnectionManager j) { +        jdbcConnectionManager = j; +    } + +    public long add(Vehicle vehicle) throws PersistenceException { +        String query1 = +                "INSERT INTO VehicleVersion (name,hasNef,constructionType,type) VALUES (?,?,?,?)"; +        String query2 = "INSERT INTO Vehicle (version,status) VALUES (?,?)"; +        PreparedStatement p1 = null; +        PreparedStatement p2 = null; +        PreparedStatement p3 = null; +        String status = "ABGEMELDET"; +        String name = ""; +        int id = -1; +        try { +            p1 = +                    jdbcConnectionManager +                            .getConnection() +                            .prepareStatement(query1, PreparedStatement.RETURN_GENERATED_KEYS); +            p1.setString(1, name); +            p1.setBoolean(2, vehicle.hasNef()); +            p1.setString(3, vehicle.constructionType().name()); +            if (vehicle.type() == VehicleType.KTW_B) { +                p1.setString(4, "KTW-B"); +            } else { +                p1.setString(4, vehicle.type().name()); +            } +            p1.executeUpdate(); + +            ResultSet keyResultSet = p1.getGeneratedKeys(); + +            if (keyResultSet.next()) { +                id = keyResultSet.getInt(1); +            } + +            name = vehicle.type().name() + "-" + id; + +        } catch (SQLException e) { +            throw new PersistenceException("SQL Excpetion : " + e.toString()); +        } finally { +            try { +                p1.close(); + +            } catch (SQLException e) { +                throw new PersistenceException("SQL Excpetion : " + e.toString()); +            } +        } +        try { +            query1 = "UPDATE VehicleVersion SET name=? WHERE id=?"; +            p3 = jdbcConnectionManager.getConnection().prepareStatement(query1); +            p3.setString(1, name); +            p3.setInt(2, id); +            p3.executeUpdate(); +        } catch (SQLException e) { +            throw new PersistenceException("SQL Excpetion : " + e.toString()); +        } finally { +            try { +                p3.close(); +            } catch (SQLException e) { +                throw new PersistenceException("SQL Excpetion : " + e.toString()); +            } +        } +        try { +            p2 = jdbcConnectionManager.getConnection().prepareStatement(query2); +            p2.setInt(1, id); +            p2.setString(2, status); +            p2.executeUpdate(); +        } catch (SQLException e) { +            throw new PersistenceException("SQL Excpetion : " + e.toString()); +        } finally { +            try { +                p2.close(); +            } catch (SQLException e) { +                throw new PersistenceException("SQL Excpetion : " + e.toString()); +            } +        } +        return id; +    } + +    @Override +    public void update(Vehicle vehicle) throws ElementNotFoundException, PersistenceException {} + +    @Override +    public List<Vehicle> list() throws PersistenceException { +        PreparedStatement pstmt = null; +        List<Vehicle> result = new LinkedList<>(); +        try { +            pstmt = +                    jdbcConnectionManager +                            .getConnection() +                            .prepareStatement( +                                    "Select * from VehicleVersion, " +                                            + "Vehicle where VehicleVersion.id=Vehicle.version"); +            pstmt.executeQuery(); +            ResultSet rs = pstmt.getResultSet(); +            while (rs.next()) { +                Vehicle vehicle = +                        Vehicle.builder() +                                .name(rs.getString("name")) +                                .constructionType( +                                        ConstructionType.valueOf(rs.getString("constructionType"))) +                                .status(Status.valueOf(rs.getString("status"))) +                                .id(rs.getInt("id")) +                                .hasNef(rs.getBoolean("hasNef")) +                                .type(VehicleType.valueOf(rs.getString("type").replace("-", "_"))) +                                .build(); +                result.add(vehicle); +            } +        } catch (SQLException e) { +            throw new PersistenceException("Die Werte konnten nicht geladen werden.", e); +        } finally { +            if (pstmt != null) { +                try { +                    pstmt.close(); +                } catch (SQLException e) { +                    throw new PersistenceException( +                            "Verbindung zur Datenbank konnte nicht geschlossen werden!", e); +                } +            } +        } +        return result; +    } + +    @Override +    public void remove(long id) throws ElementNotFoundException, PersistenceException {} +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Employee.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Employee.java index bbb5117..583bf5b 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Employee.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Employee.java @@ -14,17 +14,17 @@ public abstract class Employee {          NA      } -    abstract long id(); +    public abstract long id(); -    abstract String name(); +    public abstract String name(); -    abstract LocalDate birthday(); +    public abstract LocalDate birthday(); -    abstract EducationLevel educationLevel(); +    public abstract EducationLevel educationLevel(); -    abstract boolean isDriver(); +    public abstract boolean isDriver(); -    abstract boolean isPilot(); +    public abstract boolean isPilot();      public static Builder builder() {          return new AutoValue_Employee.Builder().id(0); diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/EmployeeValidator.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/EmployeeValidator.java new file mode 100644 index 0000000..d7fa9aa --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/EmployeeValidator.java @@ -0,0 +1,23 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto; + +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException; + +public class EmployeeValidator { + +    public static boolean validate(Employee employee) throws InvalidEmployeeException { + +        if (employee.name() == null || employee.name().trim().length() == 0) { +            throw new InvalidEmployeeException("name not set"); +        } + +        if (employee.birthday() == null) { +            throw new InvalidEmployeeException("birthday not set"); +        } + +        if (employee.educationLevel() == null) { +            throw new InvalidEmployeeException("educationLevel not set"); +        } + +        return true; +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Operation.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Operation.java index bfb03c7..6641437 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Operation.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Operation.java @@ -22,22 +22,22 @@ public abstract class Operation {          CANCELLED,      } -    abstract long id(); +    public abstract long id(); -    abstract String opCode(); +    public abstract String opCode(); -    abstract Severity severity(); +    public abstract Severity severity(); -    abstract Status status(); +    public abstract Status status(); -    abstract List<Vehicle> vehicles(); +    public abstract List<Vehicle> vehicles();      @Nullable -    abstract Instant created(); +    public abstract Instant created(); -    abstract String destination(); +    public abstract String destination(); -    abstract String additionalInfo(); +    public abstract String additionalInfo();      public static Builder builder() {          return new AutoValue_Operation.Builder().id(0); diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Registration.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Registration.java index f917406..8551266 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Registration.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Registration.java @@ -5,13 +5,13 @@ import java.time.Instant;  @AutoValue  public abstract class Registration { -    abstract long id(); +    public abstract long id(); -    abstract Instant start(); +    public abstract Instant start(); -    abstract Instant end(); +    public abstract Instant end(); -    abstract Employee employee(); +    public abstract Employee employee();      public static Builder builder() {          return new AutoValue_Registration.Builder().id(0); diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/RegistrationValidator.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/RegistrationValidator.java new file mode 100644 index 0000000..295b615 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/RegistrationValidator.java @@ -0,0 +1,194 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.VehicleType; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RegistrationValidator { + +    private static final Logger LOG = LoggerFactory.getLogger(RegistrationValidator.class); + +    private RegistrationValidator() {} + +    public static void validate(Vehicle vehicle, List<Registration> registrations) +            throws InvalidVehicleException, InvalidRegistrationException { +        /* +        Vehicles and Employees are assumed to be valid. +        They have been checked at creation, and for them to be checked again, access to +        VehicleValidator and EmployeeValidator are needed, which are not available at this time. +         */ +        /* +        The method used here goes as follows: All given employees are inspected in regards to their +        qualifications, and added to the appropriate lists of the roles they could fill. +        For example, an NFS, who is also a driver, would be added to the lists driverIds, nfsIds +        and rsIds (because an NFS can always substitute an RS). +        Afterwards, the number of people is checked according to the chosen vehicle type, and if +        the number is okay, the program tries to find a valid combination of roles for the vehicle. +        For example, for an RTW, first a driver is chosen, their ID marked as found in the aptly +        titled HashMap, and then for the second RS, the list of RS is checked, excluding the chosen +        driver. If no other valid RS is found, the next possible driver is chosen, and so on. If no +        valid combination is found, an InvalidRegistrationException is thrown. +         */ +        List<Long> pilotIds = new LinkedList<>(); +        List<Long> driverIds = new LinkedList<>(); +        List<Long> naIds = new LinkedList<>(); +        List<Long> nfsIds = new LinkedList<>(); +        List<Long> rsIds = new LinkedList<>(); +        HashMap<Long, Boolean> found = +                new HashMap<>(); // needed later in DFS, checks that no person is chosen twice +        int total = 0; +        for (Registration registration : registrations) { +            total++; +            if (found.put(registration.employee().id(), false) != null) { +                LOG.info("Employee with ID {} was added twice", registration.employee().id()); +                throw new InvalidRegistrationException( +                        "Person with the ID: " +                                + registration.employee().id() +                                + " was added more than once!"); +            } +            if (registration.employee().isPilot()) { +                pilotIds.add(registration.employee().id()); +            } +            if (registration.employee().isDriver()) { +                driverIds.add(registration.employee().id()); +            } +            if (registration.employee().educationLevel() == EducationLevel.NA) { +                naIds.add(registration.employee().id()); +                nfsIds.add(registration.employee().id()); +                rsIds.add(registration.employee().id()); +            } else if (isNFS(registration.employee())) { +                nfsIds.add(registration.employee().id()); +                rsIds.add(registration.employee().id()); +            } else { // only RS left +                rsIds.add(registration.employee().id()); +            } +        } +        if (total <= 0) { +            LOG.info("No employees were added"); +            throw new InvalidRegistrationException("Kein Personal ausgewählt!"); +        } +        if (vehicle.type() == VehicleType.NAH) { +            /* +            NAH +            1 Pilot +            1 NFS +            1 NA +            3-4 Personen +             */ +            if (total < 3) { +                LOG.info("Too few employees for NAH"); +                throw new InvalidRegistrationException("Zu wenig Personal für NAH!"); +            } else if (total > 4) { +                LOG.info("Too many employees for NAH"); +                throw new InvalidRegistrationException("Zu viel Personal für NAH!"); +            } +            for (long pilot_id : pilotIds) { +                found.put(pilot_id, true); +                for (long na_id : naIds) { +                    if (found.get(na_id)) continue; +                    found.put(na_id, true); +                    for (long nfs_id : nfsIds) { +                        if (found.get(nfs_id)) continue; +                        LOG.info("Valid combination found for NAH"); +                        return; +                    } +                    found.put(na_id, false); +                } +                found.put(pilot_id, false); +            } +            LOG.info("No valid combination of employees found for NAH"); +            throw new InvalidRegistrationException( +                    "Keine gültige Kombination von Personen für NAH!"); +        } else if (vehicle.type() == VehicleType.NEF) { +            /* +            NEF +            1 Driver (has to be NFS) +            1 NA +             */ +            if (total < 2) { +                LOG.info("Too few employees for NEF"); +                throw new InvalidRegistrationException("Zu wenig Personal für NEF!"); +            } else if (total > 3) { +                LOG.info("Too many employees for NEF"); +                throw new InvalidRegistrationException("Zu viel Personal für NEF!"); +            } +            for (long driver_id : driverIds) { +                if (!nfsIds.contains(driver_id)) +                    continue; // if possible driver is not NFS, skip him +                found.put(driver_id, true); +                for (long na_id : naIds) { +                    if (found.get(na_id)) continue; +                    LOG.info("Valid combinaion found for NEF"); +                    return; +                } +                found.put(driver_id, false); +            } +            LOG.info("No valid combination of employees found for NEF"); +            throw new InvalidRegistrationException( +                    "Keine gültige Kombination von Personen für NEF!"); +        } else if (vehicle.type() == VehicleType.BKTW) { +            /* +            BKTW +            1 Driver +             */ +            if (total > 3) { +                LOG.info("Too many employees for BKTW"); +                throw new InvalidRegistrationException("Zu viel Personal für BKTW!"); +            } +            if (!driverIds.isEmpty()) { +                LOG.info("Valid combination found for BKTW"); +                return; +            } +            LOG.info("No driver was found for BKTW"); +            throw new InvalidRegistrationException("Kein Fahrer gefunden für BKTW!"); +        } else { // KTW or RTW, both have the same requirements +            /* +            RTW/KTW +            1 Driver +            1 RS +             */ +            if (total < 2) { +                LOG.info("Too few employees for {}", vehicle.type().name()); +                throw new InvalidRegistrationException( +                        "Zu wenig Personal für " + vehicle.type().name() + "!"); +            } else if (total > 4) { +                LOG.info("Too many employees for {}", vehicle.type().name()); +                throw new InvalidRegistrationException( +                        "Zu viel Persoanl für " + vehicle.type().name() + "!"); +            } +            for (long driver_id : driverIds) { // driver includes rs +                found.put(driver_id, true); +                for (long rs_id : rsIds) { +                    if (found.get(rs_id)) continue; +                    LOG.info("Valid combination found for {}", vehicle.type().name()); +                    return; +                } +            } +            LOG.info("No valid combination of employees found for {}", vehicle.type().name()); +            throw new InvalidRegistrationException( +                    "Keine gültige Kombination von Personen für " + vehicle.type().name() + "!"); +        } +    } + +    private static boolean isNFS(Employee employee) { +        EducationLevel educationLevel = employee.educationLevel(); +        switch (educationLevel) { +            case NFS: +                return true; +            case NKA: +                return true; +            case NKI: +                return true; +            case NKV: +                return true; +            default: +                return false; +        } +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Vehicle.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Vehicle.java index 29698da..84d9c92 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Vehicle.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dto/Vehicle.java @@ -31,20 +31,20 @@ public abstract class Vehicle {          FREI_FUNK,      } -    abstract long id(); +    public abstract long id(); -    abstract String name(); +    public abstract String name(); -    abstract ConstructionType constructionType(); +    public abstract ConstructionType constructionType(); -    abstract VehicleType type(); +    public abstract VehicleType type(); -    abstract Status status(); +    public abstract Status status(); -    abstract boolean hasNef(); +    public abstract boolean hasNef();      @Nullable -    abstract List<Registration> registrations(); +    public abstract List<Registration> registrations();      public static Builder builder() {          return new AutoValue_Vehicle.Builder().id(0); diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/EmployeeServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/EmployeeServiceImpl.java new file mode 100644 index 0000000..ed0fb1c --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/EmployeeServiceImpl.java @@ -0,0 +1,49 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.EmployeeDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.EmployeeValidator; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.util.List; +import org.springframework.stereotype.Service; + +@Service +public class EmployeeServiceImpl implements EmployeeService { + +    private final EmployeeDAO employeePersistence; + +    public EmployeeServiceImpl(EmployeeDAO employeePersistence) { +        this.employeePersistence = employeePersistence; +    } + +    @Override +    public long add(Employee employee) throws InvalidEmployeeException, ServiceException { + +        EmployeeValidator.validate(employee); +        try { +            return employeePersistence.add(employee); +        } catch (PersistenceException e) { +            throw new ServiceException(e); +        } +    } + +    @Override +    public Employee update(Employee employee) throws InvalidEmployeeException, ServiceException { +        return null; +    } + +    @Override +    public List<Employee> list() throws ServiceException { + +        try { +            return employeePersistence.list(); +        } catch (PersistenceException e) { +            throw new ServiceException(e); +        } +    } + +    @Override +    public void remove(long id) throws InvalidEmployeeException, ServiceException {} +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/OperationServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/OperationServiceImpl.java new file mode 100644 index 0000000..05a548c --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/OperationServiceImpl.java @@ -0,0 +1,131 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.OperationDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation.Severity; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation.Status; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.util.EnumSet; +import java.util.List; +import javafx.collections.transformation.SortedList; + +public class OperationServiceImpl implements OperationService { + +    // TODO: anders? +    private OperationDAO operationDAO; + +    public OperationServiceImpl(OperationDAO dao) { +        this.operationDAO = dao; +    } + +    @Override +    public long add(Operation operation) throws InvalidOperationException, ServiceException { +        List<Vehicle> vehicles = operation.vehicles(); +        boolean rtw = false; +        if (faultyInput(operation.opCode())) { +            throw new InvalidOperationException("Code ist ungültig!"); +        } +        if (faultyInput(operation.destination())) { +            throw new InvalidOperationException("Adresse ist ungültig!"); +        } +        if (operation.vehicles().size() == 0) { +            throw new InvalidOperationException( +                    "Es muss mindestens ein Fahrzeug ausgewählt werden!"); +        } +        for (Vehicle vehicle : vehicles) { +            if (vehicle.status() == Vehicle.Status.ABGEMELDET +                    || (vehicle.status() != Vehicle.Status.FREI_FUNK +                            && vehicle.status() != Vehicle.Status.FREI_WACHE)) +                throw new InvalidOperationException( +                        "Abgemeldete Fahrzeuge dürfen nicht zu einem Einsatz geschickt werden!"); +            /*if (vehicle.type() == VehicleType.NEF && !rtw) { +                for (Vehicle vehicleA : vehicles) { +                    if (vehicleA.type() == VehicleType.RTW && vehicleA.hasNef()) { +                        rtw = true; +                        break; +                    } +                } +                if (!rtw) +                    throw new InvalidOperationException( +                            "Zu einem Fahrzeug des Typs NEF muss auch ein Fahrzeug des Typs RTW mit NEF-Halterung geschickt werden!"); +            }*/ +            /* if (vehicle.type() == VehicleType.NAH && !rtw) { +                for (Vehicle vehicleA : vehicles) { +                    if (vehicleA.type() == VehicleType.RTW) { +                        rtw = true; +                        break; +                    } +                } +                if (!rtw) +                    throw new InvalidOperationException( +                            "Zu einem Fahrzeug des Typs NAH muss auch ein Fahrzeug des Typs RTW geschickt werden!"); +            }*/ +        } +        String[] codeParts = operation.opCode().split("-"); +        String severity = ""; +        for (int i = 0; i < codeParts[1].length(); i++) { +            if (((int) (codeParts[1].charAt(i)) >= 65 && (int) (codeParts[1].charAt(i)) <= 79) +                    || ((int) (codeParts[1].charAt(i)) >= 97 +                            && (int) (codeParts[1].charAt(i)) <= 111)) { +                severity = "" + codeParts[1].charAt(i); +                break; +            } +        } +        try { +            operation = operation.toBuilder().severity(Severity.valueOf(severity)).build(); +        } catch (IllegalArgumentException e) { +            throw new InvalidOperationException( +                    "Der Schweregrad des Einsatzes konnte nicht ausgelesen werden!"); +        } +        operation = operation.toBuilder().status(Status.ACTIVE).build(); + +        long operationId = -1; +        try { +            operationId = operationDAO.add(operation); +        } catch (PersistenceException e) { +            throw new ServiceException(e); +        } + +        for (Vehicle vehicle : vehicles) { +            try { +                operationDAO.connectVehicleToOperation(vehicle.id(), operationId); +            } catch (PersistenceException e) { +                throw new ServiceException(e); +            } +        } + +        return operationId; +    } + +    private boolean faultyInput(String name) { +        if (name == null) return true; +        else if (name.isEmpty()) return true; +        for (int i = 0; i < name.length(); i++) { +            if (name.charAt(i) != ' ') return false; +        } +        return true; +    } + +    @Override +    public void requestVehicles(long operationId, List<Long> vehicleIds) +            throws InvalidOperationException, InvalidVehicleException, ServiceException {} + +    @Override +    public void complete(long operationId, Status status) +            throws InvalidOperationException, ServiceException {} + +    @Override +    public SortedList<Vehicle> rankVehicles(long operationId) +            throws InvalidOperationException, ServiceException { +        return null; +    } + +    @Override +    public List<Operation> list(EnumSet<Status> statuses) throws ServiceException { +        return null; +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationService.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationService.java index 6723f32..c345a2b 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationService.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationService.java @@ -4,6 +4,7 @@ import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registratio  import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException;  import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException;  import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.util.List;  public interface RegistrationService { @@ -11,13 +12,13 @@ public interface RegistrationService {       * Register employee to a vehicle.       *       * @param vehicleId the id of the target vehicle -     * @param registration that should be added to the vehicle -     * @return the id that was assigned +     * @param registrations that should be added to the vehicle +     * @return the list of ids that were assigned       * @throws InvalidVehicleException if the vehicleId is invalid or does not exist       * @throws InvalidRegistrationException if the registration is invalid       * @throws ServiceException if the registration could not be persisted       */ -    long add(long vehicleId, Registration registration) +    List<Long> add(long vehicleId, List<Registration> registrations)              throws InvalidVehicleException, InvalidRegistrationException, ServiceException;      /** diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationServiceImpl.java new file mode 100644 index 0000000..a267b6f --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationServiceImpl.java @@ -0,0 +1,62 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.RegistrationDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registration; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.RegistrationValidator; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.Status; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.util.EnumSet; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class RegistrationServiceImpl implements RegistrationService { + +    private static final Logger LOG = LoggerFactory.getLogger(RegistrationServiceImpl.class); + +    private final RegistrationDAO registrationDAO; +    private final VehicleService vehicleService; + +    @Autowired +    public RegistrationServiceImpl(RegistrationDAO registrationDAO, VehicleService vehicleService) { +        this.registrationDAO = registrationDAO; +        this.vehicleService = vehicleService; +    } + +    @Override +    public List<Long> add(long vehicleId, List<Registration> registrations) +            throws InvalidVehicleException, InvalidRegistrationException, ServiceException { + +        Vehicle vehicle = +                vehicleService +                        .list(EnumSet.of(Status.ABGEMELDET)) +                        .stream() +                        .filter(v -> v.id() == vehicleId) +                        .findFirst() +                        .orElse(null); + +        if (vehicle == null) { +            throw new ServiceException("no vehicle with this id"); +        } + +        RegistrationValidator.validate(vehicle, registrations); +        try { +            return registrationDAO.add(vehicle.id(), registrations); +        } catch (PersistenceException e) { +            LOG.warn("PersistenceException caught, throwing matching ServiceException"); +            throw new ServiceException(e); +        } +    } + +    @Override +    public void remove(long registrationId) throws InvalidRegistrationException, ServiceException { +        throw new UnsupportedOperationException(); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/VehicleServiceImpl.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/VehicleServiceImpl.java new file mode 100644 index 0000000..bbe668b --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/VehicleServiceImpl.java @@ -0,0 +1,99 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.VehicleDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.ConstructionType; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.Status; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; + +@Service +public class VehicleServiceImpl implements VehicleService { + +    private VehicleDAO vehiclePersistence; + +    public VehicleServiceImpl(VehicleDAO vehiclePersistence) { +        this.vehiclePersistence = vehiclePersistence; +    } + +    public long add(Vehicle vehicle) throws InvalidVehicleException, ServiceException { + +        switch (vehicle.type()) { +            case RTW: +                if (vehicle.constructionType() == ConstructionType.NORMAL) { +                    throw new InvalidVehicleException("RTW darf kein Normales Dach haben"); +                } else if (vehicle.constructionType() == ConstructionType.MITTELHOCHDACH) { +                    throw new InvalidVehicleException("RTW darf kein Mittelhochdach haben"); +                } +                break; +            case KTW: +                if (vehicle.constructionType() == ConstructionType.NORMAL) { +                    throw new InvalidVehicleException("KTW darf kein Normales Dach haben"); +                } +                break; +            case KTW_B: +                if (vehicle.constructionType() == ConstructionType.NORMAL) { +                    throw new InvalidVehicleException("KTW-B darf kein Normales Dach haben"); +                } +                break; +            case NEF: +                if (vehicle.constructionType() == ConstructionType.MITTELHOCHDACH) { +                    throw new InvalidVehicleException("NEF darf kein Mittelhochdach haben"); +                } else if (vehicle.constructionType() == ConstructionType.HOCHDACH) { +                    throw new InvalidVehicleException("NEF darf kein Hochdach haben"); +                } +                break; +            case NAH: +                if (vehicle.constructionType() == ConstructionType.MITTELHOCHDACH) { +                    throw new InvalidVehicleException("NEF darf kein Mittelhochdach haben"); +                } else if (vehicle.constructionType() == ConstructionType.HOCHDACH) { +                    throw new InvalidVehicleException("NEF darf kein Hochdach haben"); +                } +                break; +            case BKTW: +                break; +            default: +                throw new ServiceException("not a Valid type"); +        } +        try { +            vehiclePersistence.add(vehicle); +        } catch (PersistenceException e) { +            throw new ServiceException(e); +        } +        return 0; +    } + +    public Vehicle update(Vehicle vehicle) throws InvalidVehicleException, ServiceException { +        throw new UnsupportedOperationException(); +    } + +    @Override +    public List<Vehicle> list(EnumSet<Status> statuses) throws ServiceException { + +        if (statuses == null) { +            throw new ServiceException("statuses may not be null"); +        } + +        List<Vehicle> vehicles; + +        try { +            vehicles = vehiclePersistence.list(); +        } catch (PersistenceException e) { +            throw new ServiceException(e); +        } + +        return vehicles.stream() +                .filter(vehicle -> statuses.contains(vehicle.status())) +                .collect(Collectors.toList()); +    } + +    @Override +    public void remove(long id) throws InvalidVehicleException, ServiceException { +        throw new UnsupportedOperationException(); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/ui/vehiclepane/VehiclePaneController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/ui/vehiclepane/VehiclePaneController.java new file mode 100644 index 0000000..2db6f37 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/ui/vehiclepane/VehiclePaneController.java @@ -0,0 +1,84 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.ui.vehiclepane; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registration; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import java.io.IOException; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.text.Text; + +public class VehiclePaneController { +    @FXML private Text txtType; +    @FXML private Text txtNumber; +    @FXML private ImageView ivNEF; +    @FXML private Text txtNEF; +    @FXML private ImageView ivQualification; +    @FXML private Text txtQualification; +    @FXML private Text txtRooftype; + +    public static VehiclePaneController createVehiclePane() throws IOException { +        FXMLLoader fxmlLoader = +                new FXMLLoader(VehiclePaneController.class.getResource("/fxml/vehiclePane.fxml")); +        Node root = fxmlLoader.load(); +        VehiclePaneController result = fxmlLoader.getController(); +        result.rootElement = root; + +        return result; +    } + +    private Node rootElement; + +    public Node getRootElement() { +        return rootElement; +    } + +    /** +     * * Set the displayed data of this VehiclePane. +     * +     * @param vehicle The data to display. +     * @param showQualification If true, the most recent registration of vehicle will be searched +     *     for the highest qualification. +     */ +    public void setData(Vehicle vehicle, boolean showQualification) { +        txtType.setText(vehicle.type().name()); +        String constrType = vehicle.constructionType().name(); +        txtRooftype.setText( +                constrType.substring(0, 1).toUpperCase() + constrType.substring(1).toLowerCase()); +        txtNumber.setText("" + vehicle.id()); +        if (vehicle.hasNef()) { +            ivNEF.setImage(new Image("../images/NEF.png")); +            txtNEF.setText("hat NEF-Halterung"); +        } else { +            ivNEF.setImage(new Image("../images/NotNEF.png")); +            txtNEF.setText("keine NEF-Halterung"); +        } +        if (showQualification) { + +            Instant now = (new Date()).toInstant(); +            List<Registration> regs = vehicle.registrations(); + +            assert regs != null; +            Optional<EducationLevel> edu = +                    regs.stream() +                            .filter(reg -> reg.start().isBefore(now) && reg.end().isAfter(now)) +                            .map(reg -> reg.employee().educationLevel()) +                            .max(EducationLevel::compareTo); + +            assert edu.isPresent(); +            txtQualification.setText(edu.get().name()); +        } else { +            txtQualification.setVisible(false); +            txtQualification.setManaged(false); +            ivQualification.setVisible(false); +            ivQualification.setManaged(false); +        } +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/userInterface/CreateOperationController.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/userInterface/CreateOperationController.java new file mode 100644 index 0000000..5b645f3 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/userInterface/CreateOperationController.java @@ -0,0 +1,227 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.userInterface; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.DBOperationDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation.Severity; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation.Status; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.OperationService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.OperationServiceImpl; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleService; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager; +import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.time.Instant; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import javafx.collections.FXCollections; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; + +@Controller +public class CreateOperationController { + +    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + +    public AnchorPane apCreateOperation; +    public TextField txtCode; +    public TextField txtAddress; +    public TextField txtNote; +    public Button btnCreateOperation; +    public ListView<Vehicle> lvVehicles; +    public ListView lvActiveOperations; +    public Label lblChosenVehicles; +    public LinkedList<Vehicle> chosenVehicles = new LinkedList<>(); + +    // TODO: Anders? +    OperationService operationService = +            new OperationServiceImpl(new DBOperationDAO(new JDBCConnectionManager())); +    private final VehicleService vehicleService; +    private final SpringFXMLLoader fxmlLoader; + +    public CreateOperationController(VehicleService vehicleService, SpringFXMLLoader fxmlLoader) { +        this.vehicleService = vehicleService; +        this.fxmlLoader = fxmlLoader; +    } + +    @FXML +    public void initialize() { +        lblChosenVehicles.setText("keine ausgewählt"); +        lvVehicles.setCellFactory( +                param -> +                        new ListCell<Vehicle>() { +                            @Override +                            protected void updateItem(Vehicle item, boolean empty) { +                                super.updateItem(item, empty); + +                                if (empty || item == null || item.name() == null) { +                                    setText(null); +                                } else { +                                    setText(item.name()); +                                } +                            } +                        }); + +        lvVehicles.setOnMouseClicked( +                event -> { +                    if (event.getClickCount() == 2) { +                        boolean remove = false; +                        if (lvVehicles.getSelectionModel().getSelectedItem() == null) { +                            return; +                        } +                        for (Vehicle vehicle : chosenVehicles) { +                            if (lvVehicles.getSelectionModel().getSelectedItem().equals(vehicle)) { +                                remove = true; +                                break; +                            } +                        } +                        if (!remove) { +                            chosenVehicles.add(lvVehicles.getSelectionModel().getSelectedItem()); + +                        } else { +                            chosenVehicles.remove(lvVehicles.getSelectionModel().getSelectedItem()); +                        } +                        StringBuilder result = new StringBuilder(); +                        for (int i = 0; i < chosenVehicles.size(); i++) { +                            if (i == chosenVehicles.size() - 1) { +                                result.append(chosenVehicles.get(i).name()); +                            } else { +                                result.append(chosenVehicles.get(i).name()).append(", "); +                            } +                        } +                        if (result.toString().equals("")) { +                            lblChosenVehicles.setText("keine ausgewählt"); +                        } else { +                            lblChosenVehicles.setText(result.toString()); +                        } +                    } +                }); +    } + +    public void updateList() { +        try { +            this.lvVehicles.setItems( +                    FXCollections.observableArrayList( +                            vehicleService.list( +                                    EnumSet.of( +                                            Vehicle.Status.FREI_FUNK, Vehicle.Status.FREI_WACHE)))); +        } catch (ServiceException e) { +            Alert alert = new Alert(Alert.AlertType.ERROR); +            alert.setTitle("Fehler"); +            alert.setHeaderText("Fehler!"); +            alert.setContentText(e.getMessage()); +            alert.showAndWait(); +        } +    } + +    /*private LinkedList<Vehicle> mylist() { +        Vehicle vehicle = +                Vehicle.builder() +                        .name("Test-KTW") +                        .constructionType(ConstructionType.HOCHDACH) +                        .type(VehicleType.KTW) +                        .status(Vehicle.Status.FREI_WACHE) +                        .hasNef(true) +                        .build(); + +        Vehicle vehicle1 = +                Vehicle.builder() +                        .name("Test-NEF") +                        .constructionType(ConstructionType.NORMAL) +                        .type(VehicleType.NEF) +                        .status(Vehicle.Status.FREI_FUNK) +                        .hasNef(true) +                        .build(); +        LinkedList<Vehicle> list = new LinkedList<>(); +        list.add(vehicle); +        list.add(vehicle1); +        // this.lvVehicles.setItems(FXCollections.observableArrayList(list)); +        return list; +    }*/ + +    @FXML +    protected void createOperationClicked() { +        Vehicle[] vehicles = new Vehicle[chosenVehicles.size()]; +        for (int i = 0; i < chosenVehicles.size(); i++) { +            vehicles[i] = chosenVehicles.get(i); +        } +        Operation operation = +                Operation.builder() +                        .additionalInfo(txtNote.getText()) +                        .destination(txtAddress.getText()) +                        .created(Instant.now()) +                        .opCode(txtCode.getText()) +                        .status(Status.ACTIVE) +                        .vehicles(List.of(vehicles)) +                        .severity(Severity.A) +                        .build(); +        try { +            operationService.add(operation); +        } catch (ServiceException | InvalidOperationException e) { +            Alert alert = new Alert(Alert.AlertType.ERROR); +            alert.setTitle("Fehler"); +            alert.setHeaderText("Fehler!"); +            alert.setContentText(e.getMessage()); +            alert.showAndWait(); +            return; +        } +        Alert alert = new Alert(AlertType.CONFIRMATION); +        alert.setTitle("Erfolg"); +        alert.setHeaderText("Erfolgreich gespeichert"); +        alert.setContentText("Der Einsatz wurde erfolgreich gespeichert."); +        alert.showAndWait(); +        updateList(); +    } + +    public void onRegistrationLinkClicked(ActionEvent actionEvent) { +        openNewWindow("RegistrationWindow.fxml"); +    } + +    public void onEmployeeLinkClicked(ActionEvent actionEvent) { +        openNewWindow("createNewEmployee.fxml"); +    } + +    public void onVehicleLinkClicked(ActionEvent actionEvent) { +        openNewWindow("createCar.fxml"); +    } + +    private void openNewWindow(String fxmlFileName) { + +        Stage stage = new Stage(); +        try { +            stage.setScene( +                    new Scene( +                            (Parent) +                                    fxmlLoader.load( +                                            getClass() +                                                    .getResourceAsStream( +                                                            "/fxml/" + fxmlFileName)))); +        } catch (IOException e) { +            LOG.error("Could not open new window: {}", e); +        } + +        stage.setTitle("Einsatz erstellen"); +        stage.centerOnScreen(); +        stage.showAndWait(); // important to call wait so that updateList is executed afterwards + +        updateList(); +    } +} diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/util/JDBCConnectionManager.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/util/JDBCConnectionManager.java index 5494471..0ee3319 100644 --- a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/util/JDBCConnectionManager.java +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/util/JDBCConnectionManager.java @@ -12,12 +12,17 @@ import org.springframework.stereotype.Component;  public class JDBCConnectionManager {      private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); -    private static final String CONNECTION_URL = -            "jdbc:h2:~/sepm;INIT=RUNSCRIPT FROM 'classpath:sql/database.sql'"; - +    private static final String DEFAULT_CONNECTION_URL = +            "jdbc:h2:./sepm;INIT=RUNSCRIPT FROM 'classpath:sql/database.sql'"; +    private String connectionUrl;      private Connection connection;      public JDBCConnectionManager() { +        this(DEFAULT_CONNECTION_URL); +    } + +    public JDBCConnectionManager(String connectionUrl) { +        this.connectionUrl = connectionUrl;          try {              Class.forName("org.h2.Driver");          } catch (ClassNotFoundException e) { @@ -27,7 +32,7 @@ public class JDBCConnectionManager {      }      public Connection getConnection() throws SQLException { -        if (connection == null) connection = DriverManager.getConnection(CONNECTION_URL); +        if (connection == null) connection = DriverManager.getConnection(connectionUrl);          return connection;      } diff --git a/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/util/SpringFXMLLoader.java b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/util/SpringFXMLLoader.java new file mode 100644 index 0000000..cb1f510 --- /dev/null +++ b/src/main/java/at/ac/tuwien/sepm/assignment/groupphase/util/SpringFXMLLoader.java @@ -0,0 +1,212 @@ +package at.ac.tuwien.sepm.assignment.groupphase.util; + +import java.io.IOException; +import java.io.InputStream; +import javafx.fxml.FXMLLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * A Spring Based FXMLLoader. + * + * <p>Provides the possibility to load FXML-Files and wrap them for convenient access to the loaded + * class as well as the spring loaded controller. + */ +@Component +public class SpringFXMLLoader { + +    private Logger LOG = LoggerFactory.getLogger(SpringFXMLLoader.class); + +    private ApplicationContext applicationContext; + +    @Autowired +    public SpringFXMLLoader(ApplicationContext applicationContext) { +        this.applicationContext = applicationContext; +    } + +    private FXMLLoader getFXMLLoader() { +        final var fxmlLoader = new FXMLLoader(); +        fxmlLoader.setControllerFactory(applicationContext::getBean); +        return fxmlLoader; +    } + +    /** +     * Load and return an object from an FXML-File. +     * +     * @param inputStream the input stream of the FXML-File +     * @param loadType the class of the object to load +     * @param <TLoad> the loaded object +     * @return the loaded object +     * @throws IOException if the resource could not be loaded +     */ +    public synchronized <TLoad> TLoad load(InputStream inputStream, Class<TLoad> loadType) +            throws IOException { +        LOG.trace( +                "Loading object of type {} from fxml resource {}", +                loadType.getCanonicalName(), +                inputStream); +        return this.getFXMLLoader().load(inputStream); +    } + +    /** +     * Load and return an object from an FXML-File. +     * +     * @param pathToFXMLFile path to the FXML-File +     * @param loadType the class of the object to load +     * @param <TLoad> the loaded object +     * @return the loaded object +     * @throws IOException if the resource could not be loaded +     */ +    public <TLoad> TLoad load(String pathToFXMLFile, Class<TLoad> loadType) throws IOException { +        return this.load(SpringFXMLLoader.class.getResourceAsStream(pathToFXMLFile), loadType); +    } + +    /** +     * Load and return an object from an FXML-File. +     * +     * @param inputStream the input stream of the FXML-File +     * @return the loaded object +     * @throws IOException if the resource could not be loaded +     */ +    public Object load(InputStream inputStream) throws IOException { +        return this.load(inputStream, Object.class); +    } + +    /** +     * Load and return an object from an FXML-File. +     * +     * @param pathToFXMLFile path to the FXML-File +     * @return the loaded object +     * @throws IOException if the resource could not be loaded +     */ +    public Object load(String pathToFXMLFile) throws IOException { +        return this.load(SpringFXMLLoader.class.getResourceAsStream(pathToFXMLFile)); +    } + +    /** +     * Load and wrap an object and the declared controller from an FXML-File. +     * +     * @param inputStream the input stream of the FXML-File +     * @param loadType the class of the object to load +     * @param controllerType the class of the declared controller of the loaded object +     * @param <TLoad> the loaded object +     * @param <TController> the controller of the loaded object +     * @return a wrapper object containing the loaded object and its declared controller +     * @throws IOException if the resource could not be loaded +     * @see FXMLWrapper +     */ +    public synchronized <TLoad, TController> FXMLWrapper<TLoad, TController> loadAndWrap( +            InputStream inputStream, Class<TLoad> loadType, Class<TController> controllerType) +            throws IOException { +        final var fxmlLoader = this.getFXMLLoader(); +        LOG.trace( +                "Loading and wrapping object of type {} with controller of type {} from fxml resource {}", +                loadType.getCanonicalName(), +                controllerType.getCanonicalName(), +                inputStream); +        return new FXMLWrapper<>(fxmlLoader.load(inputStream), fxmlLoader.getController()); +    } + +    /** +     * Load and wrap an object and the declared controller from an FXML-File. +     * +     * @param pathToFXMLFile path to the FXML-File +     * @param loadType the class of the object to load +     * @param controllerType the class of the declared controller of the loaded object +     * @param <TLoad> the loaded object +     * @param <TController> the controller of the loaded object +     * @return a wrapper object containing the loaded object and its declared controller +     * @throws IOException if the resource could not be loaded +     * @see FXMLWrapper +     */ +    public synchronized <TLoad, TController> FXMLWrapper<TLoad, TController> loadAndWrap( +            String pathToFXMLFile, Class<TLoad> loadType, Class<TController> controllerType) +            throws IOException { +        return this.loadAndWrap( +                SpringFXMLLoader.class.getResourceAsStream(pathToFXMLFile), +                loadType, +                controllerType); +    } + +    /** +     * Load and wrap an object and the declared controller from an FXML-File. +     * +     * @param inputStream the input stream of the FXML-File +     * @param controllerType the class of the declared controller of the loaded object +     * @param <TController> the controller of the loaded object +     * @return a wrapper object containing the loaded object and its declared controller +     * @throws IOException if the resource could not be loaded +     * @see FXMLWrapper +     */ +    public <TController> FXMLWrapper<Object, TController> loadAndWrap( +            InputStream inputStream, Class<TController> controllerType) throws IOException { +        return this.loadAndWrap(inputStream, Object.class, controllerType); +    } + +    /** +     * Load and wrap an object and the declared controller from an FXML-File. +     * +     * @param pathToFXMLFile path to the FXML-File +     * @param controllerType the class of the declared controller of the loaded object +     * @param <TController> the controller of the loaded object +     * @return a wrapper object containing the loaded object and its declared controller +     * @throws IOException if the resource could not be loaded +     * @see FXMLWrapper +     */ +    public <TController> FXMLWrapper<Object, TController> loadAndWrap( +            String pathToFXMLFile, Class<TController> controllerType) throws IOException { +        return this.loadAndWrap(pathToFXMLFile, Object.class, controllerType); +    } + +    /** +     * Load and wrap an object and the declared controller from an FXML-File. +     * +     * @param inputStream the input stream of the FXML-File +     * @return a wrapper object containing the loaded object and its declared controller +     * @throws IOException if the resource could not be loaded +     * @see FXMLWrapper +     */ +    public FXMLWrapper<Object, Object> loadAndWrap(InputStream inputStream) throws IOException { +        return this.loadAndWrap(inputStream, Object.class, Object.class); +    } + +    /** +     * Load and wrap an object and the declared controller from an FXML-File. +     * +     * @param pathToFXMLFile path to the FXML-File +     * @return a wrapper object containing the loaded object and its declared controller +     * @throws IOException if the resource could not be loaded +     * @see FXMLWrapper +     */ +    public FXMLWrapper<Object, Object> loadAndWrap(String pathToFXMLFile) throws IOException { +        return this.loadAndWrap(pathToFXMLFile, Object.class, Object.class); +    } + +    /** +     * A wrapper for loading FXML-files and wrapping the loaded object as well as the controller. +     * +     * @param <TLoad> the loaded object +     * @param <TController> the controller of the loaded object +     */ +    public class FXMLWrapper<TLoad, TController> { + +        public TLoad getLoadedObject() { +            return loadedObject; +        } + +        public TController getController() { +            return controller; +        } + +        private final TLoad loadedObject; +        private final TController controller; + +        private FXMLWrapper(TLoad loadedObject, TController controller) { +            this.loadedObject = loadedObject; +            this.controller = controller; +        } +    } +} diff --git a/src/main/resources/fxml/CreateOperationController.fxml b/src/main/resources/fxml/CreateOperationController.fxml new file mode 100644 index 0000000..086a5d1 --- /dev/null +++ b/src/main/resources/fxml/CreateOperationController.fxml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.Hyperlink?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.ListView?> +<?import javafx.scene.control.TextField?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.text.Font?> + +<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="650.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.userInterface.CreateOperationController"> +   <children> +      <AnchorPane layoutX="964.0" layoutY="-66.0" prefHeight="182.0" prefWidth="1200.0" style="-fx-background-color: #2D75B6;" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" /> +      <AnchorPane fx:id="apCreateOperation" layoutX="40.0" layoutY="71.0" prefHeight="151.0" prefWidth="920.0" style="-fx-background-color: white; -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 5, 0, 0, 5);"> +         <children> +            <Label layoutX="14.0" layoutY="14.0" prefHeight="30.0" prefWidth="62.0" text="Code"> +               <font> +                  <Font size="19.0" /> +               </font> +            </Label> +            <Label layoutX="185.0" layoutY="14.0" prefHeight="30.0" prefWidth="94.0" text="Adresse"> +               <font> +                  <Font size="19.0" /> +               </font> +            </Label> +            <Label layoutX="587.0" layoutY="14.0" prefHeight="30.0" prefWidth="121.0" text="Anmerkung"> +               <font> +                  <Font size="19.0" /> +               </font> +            </Label> +            <TextField fx:id="txtCode" layoutX="14.0" layoutY="48.0" prefHeight="39.0" prefWidth="163.0"> +               <font> +                  <Font name="System Bold" size="20.0" /> +               </font> +            </TextField> +            <TextField fx:id="txtAddress" layoutX="185.0" layoutY="48.0" prefHeight="39.0" prefWidth="396.0"> +               <font> +                  <Font name="System Bold" size="20.0" /> +               </font> +            </TextField> +            <TextField fx:id="txtNote" layoutX="587.0" layoutY="48.0" prefHeight="39.0" prefWidth="319.0"> +               <font> +                  <Font name="System Bold" size="20.0" /> +               </font> +            </TextField> +            <Label layoutX="14.0" layoutY="101.0" prefHeight="30.0" prefWidth="102.0" text="Fahrzeuge:"> +               <font> +                  <Font size="19.0" /> +               </font> +            </Label> +            <Label fx:id="lblChosenVehicles" layoutX="116.0" layoutY="102.0" prefHeight="30.0" prefWidth="610.0" text="keine ausgewählt"> +               <font> +                  <Font size="19.0" /> +               </font> +            </Label> +            <Button fx:id="btnCreateOperation" layoutX="747.0" layoutY="95.0" mnemonicParsing="false" onAction="#createOperationClicked" prefHeight="0.0" prefWidth="158.0" text="Erstellen"> +               <font> +                  <Font name="System Bold" size="21.0" /> +               </font> +            </Button> +         </children> +      </AnchorPane> +      <Hyperlink layoutX="44.0" layoutY="38.0" onAction="#onRegistrationLinkClicked" text="Anmeldungen" textFill="WHITE"> +         <font> +            <Font size="15.0" /> +         </font> +      </Hyperlink> +      <Hyperlink layoutX="802.0" layoutY="38.0" onAction="#onEmployeeLinkClicked" text="Personen" textFill="WHITE"> +         <font> +            <Font size="15.0" /> +         </font> +      </Hyperlink> +      <Hyperlink layoutX="877.0" layoutY="38.0" onAction="#onVehicleLinkClicked" text="Fahrzeuge" textFill="WHITE"> +         <font> +            <Font size="15.0" /> +         </font> +      </Hyperlink> +      <AnchorPane fx:id="apActiveOperations" layoutX="968.0" layoutY="71.0" prefHeight="315.0" prefWidth="207.0" style="-fx-background-color: white; -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 5);"> +         <children> +            <ListView fx:id="lvActiveOperations" layoutX="4.0" layoutY="74.0" prefHeight="242.0" prefWidth="200.0" style="-fx-background-color: white;" /> +            <Label layoutX="10.0" layoutY="14.0" prefHeight="46.0" prefWidth="103.0" text="Aktive Einsätze"> +               <font> +                  <Font name="System Bold" size="14.0" /> +               </font> +            </Label> +            <Label layoutX="148.0" layoutY="28.0" text="Archiv"> +               <font> +                  <Font size="13.0" /> +               </font> +            </Label> +         </children> +      </AnchorPane> +      <ListView fx:id="lvVehicles" layoutX="40.0" layoutY="228.0" prefHeight="388.0" prefWidth="920.0" style="-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 5);" /> +   </children> +</AnchorPane> diff --git a/src/main/resources/fxml/RegistrationWindow.fxml b/src/main/resources/fxml/RegistrationWindow.fxml new file mode 100644 index 0000000..0394ca7 --- /dev/null +++ b/src/main/resources/fxml/RegistrationWindow.fxml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ChoiceBox?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.SplitPane?> +<?import javafx.scene.control.TableColumn?> +<?import javafx.scene.control.TableView?> +<?import javafx.scene.control.TextField?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.VBox?> + +<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="600.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.controller.RegistrationWindowController"> +   <children> +      <AnchorPane prefHeight="135.0" prefWidth="600.0"> +         <children> +            <Label layoutX="14.0" layoutY="14.0" text="Neue Anmeldung" /> +            <Label layoutX="14.0" layoutY="44.0" text="von" /> +            <Label layoutX="133.0" layoutY="44.0" text="bis" /> +            <ChoiceBox fx:id="cbStart" layoutX="42.0" layoutY="40.0" prefWidth="80.0" /> +            <ChoiceBox fx:id="cbEnd" layoutX="159.0" layoutY="40.0" prefWidth="80.0" /> +            <Label layoutX="10.0" layoutY="82.0" text="Fahrzeug" /> +            <Label fx:id="lVehicles" layoutX="10.0" layoutY="108.0" text="Fahrzeugname" /> +            <Label layoutX="216.0" layoutY="82.0" text="Personen" /> +            <Label fx:id="lEmployees" layoutX="216.0" layoutY="108.0" text="Namen" /> +         </children> +      </AnchorPane> +      <SplitPane dividerPositions="0.35" prefWidth="200.0"> +        <items> +            <VBox prefHeight="200.0" prefWidth="100.0"> +               <children> +                  <Label text="Fahrzeugsuche" /> +                  <TextField fx:id="tfVehicleSearch" /> +                  <TableView fx:id="tvVehicles" prefHeight="200.0" prefWidth="200.0"> +                    <columns> +                      <TableColumn fx:id="tcVehicles" prefWidth="75.0" text="Fahrzeuge" /> +                    </columns> +                     <columnResizePolicy> +                        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> +                     </columnResizePolicy> +                  </TableView> +               </children> +            </VBox> +            <VBox prefHeight="200.0" prefWidth="100.0"> +               <children> +                  <Label text="Personensuche" /> +                  <TextField fx:id="tfEmployeeSearch" /> +                  <TableView fx:id="tvEmployees" prefHeight="200.0" prefWidth="200.0"> +                    <columns> +                      <TableColumn fx:id="tcEmployees" prefWidth="75.0" text="Personen" /> +                    </columns> +                     <columnResizePolicy> +                        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> +                     </columnResizePolicy> +                  </TableView> +               </children> +            </VBox> +        </items> +      </SplitPane> +      <HBox alignment="CENTER" prefWidth="200.0"> +         <children> +            <Button mnemonicParsing="false" onAction="#cancel" text="Abbrechen"> +               <HBox.margin> +                  <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" /> +               </HBox.margin> +            </Button> +            <Button mnemonicParsing="false" onAction="#create" text="Erstellen"> +               <HBox.margin> +                  <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" /> +               </HBox.margin> +            </Button> +         </children></HBox> +   </children> +</VBox> diff --git a/src/main/resources/fxml/createCar.fxml b/src/main/resources/fxml/createCar.fxml new file mode 100644 index 0000000..b0898da --- /dev/null +++ b/src/main/resources/fxml/createCar.fxml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.CheckBox?> +<?import javafx.scene.control.ChoiceBox?> +<?import javafx.scene.layout.AnchorPane?> + + +<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.controller.CreateCarController"> +   <children> +      <ChoiceBox fx:id="cmb_Ctyp" layoutX="14.0" layoutY="14.0" prefWidth="150.0" /> +      <ChoiceBox fx:id="cmb_typ" layoutX="191.0" layoutY="14.0" prefWidth="150.0" /> +      <Button fx:id="btn_cancel" layoutX="500.0" layoutY="14.0" mnemonicParsing="false" text="abbrechen" /> +      <Button fx:id="btn_create" layoutX="500.0" layoutY="53.0" mnemonicParsing="false" onAction="#createCar" text="Erstellen" /> +      <CheckBox fx:id="cbx_NEF" layoutX="14.0" layoutY="57.0" mnemonicParsing="false" text="NEF - Halterung" /> +   </children> +</AnchorPane> diff --git a/src/main/resources/fxml/createNewEmployee.fxml b/src/main/resources/fxml/createNewEmployee.fxml new file mode 100644 index 0000000..5fa1ca9 --- /dev/null +++ b/src/main/resources/fxml/createNewEmployee.fxml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.CheckBox?> +<?import javafx.scene.control.ChoiceBox?> +<?import javafx.scene.control.Hyperlink?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.TextField?> +<?import javafx.scene.layout.AnchorPane?> + + +<AnchorPane prefHeight="114.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.controller.CreateNewEmployeeController"> +   <children> +      <Label layoutX="14.0" layoutY="14.0" text="Neue Person erstellen" /> +      <Label layoutX="14.0" layoutY="38.0" text="Name" /> +      <Label layoutX="206.0" layoutY="38.0" text="Qualifikation" /> +      <CheckBox fx:id="inputIsDriver" layoutX="343.0" layoutY="37.0" mnemonicParsing="false" text="Fahrer" /> +      <CheckBox fx:id="inputIsPilot" layoutX="343.0" layoutY="62.0" mnemonicParsing="false" text="Pilot" /> +      <Hyperlink fx:id="btnCancel" layoutX="441.0" layoutY="31.0" onAction="#onCancelClicked" text="abbrechen" /> +      <Button fx:id="btnCreate" layoutX="441.0" layoutY="55.0" mnemonicParsing="false" onAction="#onCreateClicked" text="Erstellen" /> +      <TextField fx:id="inputName" layoutX="14.0" layoutY="57.0" prefHeight="25.0" prefWidth="179.0" /> +      <ChoiceBox fx:id="inputQualification" layoutX="199.0" layoutY="57.0" prefHeight="25.0" prefWidth="128.0" /> +   </children> +</AnchorPane> diff --git a/src/main/resources/fxml/vehiclePane.fxml b/src/main/resources/fxml/vehiclePane.fxml new file mode 100644 index 0000000..8b1d194 --- /dev/null +++ b/src/main/resources/fxml/vehiclePane.fxml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.text.Font?> +<?import javafx.scene.text.Text?> +<?import javafx.scene.text.TextFlow?> + +<GridPane hgap="6.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" +  fx:controller="at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.ui.vehiclepane.VehiclePaneController"> +  <columnConstraints> +    <ColumnConstraints/> +    <ColumnConstraints/> +    <ColumnConstraints/> +    <ColumnConstraints/> +  </columnConstraints> +  <rowConstraints> +    <RowConstraints/> +    <RowConstraints/> +    <RowConstraints/> +    <RowConstraints/> +  </rowConstraints> +  <padding> +    <Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/> +  </padding> +  <TextFlow GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="0"> +    <Text text="RTW" fx:id="txtType"> +      <font> +        <Font name="System Bold" size="18.0"/> +      </font> +    </Text> +    <Text text="-10003" fx:id="txtNumber"> +      <font> +        <Font size="16.0"/> +      </font> +    </Text> +  </TextFlow> +  <ImageView fx:id="ivNEF" fitHeight="25.0" fitWidth="25.0" pickOnBounds="true" preserveRatio="true" +    GridPane.columnIndex="0" GridPane.rowIndex="1"> +    <Image url="@../images/NotNEF.png"/> +  </ImageView> +  <Text fx:id="txtNEF" text="keine NEF-Halterung" GridPane.columnIndex="1" GridPane.rowIndex="1"> +    <font> +      <Font size="14.0"/> +    </font> +  </Text> +  <ImageView fx:id="ivQualification" fitHeight="25.0" fitWidth="25.0" pickOnBounds="true" +    preserveRatio="true" +    GridPane.columnIndex="0" GridPane.rowIndex="2"> +    <Image url="@../images/Qualification.png"/> +  </ImageView> +  <Text fx:id="txtQualification" text="Notarzt" GridPane.columnIndex="1" GridPane.rowIndex="2"> +    <font> +      <Font size="14.0"/> +    </font> +  </Text> +  <ImageView fitHeight="25.0" fitWidth="25.0" pickOnBounds="true" preserveRatio="true" +    GridPane.columnIndex="0" GridPane.rowIndex="3"> +    <Image url="@../images/Vehicle.png"/> +  </ImageView> +  <Text fx:id="txtRooftype" text="Hochdach" GridPane.columnIndex="1" GridPane.rowIndex="3"> +    <font> +      <Font size="14.0"/> +    </font> +  </Text> +</GridPane> diff --git a/src/main/resources/images/NEF.png b/src/main/resources/images/NEF.png Binary files differnew file mode 100644 index 0000000..687f914 --- /dev/null +++ b/src/main/resources/images/NEF.png diff --git a/src/main/resources/images/Not.png b/src/main/resources/images/Not.png Binary files differnew file mode 100644 index 0000000..03063af --- /dev/null +++ b/src/main/resources/images/Not.png diff --git a/src/main/resources/images/NotNEF.png b/src/main/resources/images/NotNEF.png Binary files differnew file mode 100644 index 0000000..0c17d53 --- /dev/null +++ b/src/main/resources/images/NotNEF.png diff --git a/src/main/resources/images/Qualification.png b/src/main/resources/images/Qualification.png Binary files differnew file mode 100644 index 0000000..c58a640 --- /dev/null +++ b/src/main/resources/images/Qualification.png diff --git a/src/main/resources/images/Vehicle.png b/src/main/resources/images/Vehicle.png Binary files differnew file mode 100644 index 0000000..2fe992d --- /dev/null +++ b/src/main/resources/images/Vehicle.png diff --git a/src/main/resources/sql/H2RegistrationDAOTest_depopulate.sql b/src/main/resources/sql/H2RegistrationDAOTest_depopulate.sql new file mode 100644 index 0000000..f43b641 --- /dev/null +++ b/src/main/resources/sql/H2RegistrationDAOTest_depopulate.sql @@ -0,0 +1,5 @@ +DELETE FROM Registration; +DELETE FROM Vehicle; +DELETE FROM VehicleVersion; +DELETE FROM Employee; +DELETE FROM EmployeeVersion;
\ No newline at end of file diff --git a/src/main/resources/sql/H2RegistrationDAOTest_populate.sql b/src/main/resources/sql/H2RegistrationDAOTest_populate.sql new file mode 100644 index 0000000..7e7b428 --- /dev/null +++ b/src/main/resources/sql/H2RegistrationDAOTest_populate.sql @@ -0,0 +1,15 @@ +DELETE FROM Registration; +DELETE FROM Vehicle; +DELETE FROM VehicleVersion; +DELETE FROM Employee; +DELETE FROM EmployeeVersion; +INSERT INTO EmployeeVersion (id, name, birthday, educationLevel, isDriver, isPilot) VALUES (1, 'John Doe', '2000-01-01', 'RS', TRUE, TRUE); +INSERT INTO EmployeeVersion (id, name, birthday, educationLevel, isDriver, isPilot) VALUES (2, 'Nick "Kage" Verily', '1990-01-01', 'NKV', TRUE, FALSE); +INSERT INTO EmployeeVersion (id, name, birthday, educationLevel, isDriver, isPilot) VALUES (3, 'Nicht Arzt', '1980-01-01', 'NA', FALSE, FALSE); +INSERT INTO Employee (id, version) VALUES (1, 1); +INSERT INTO Employee (id, version) VALUES (2, 2); +INSERT INTO Employee (id, version) VALUES (3, 3); +INSERT INTO VehicleVersion (id, name, hasNef, constructionType, type) VALUES (1, 'RTW-1', TRUE, 'HOCHDACH', 'RTW'); +INSERT INTO VehicleVersion (id, name, hasNef, constructionType, type) VALUES (2, 'NEF-1', FALSE, 'NORMAL', 'NEF'); +INSERT INTO Vehicle (id, version, status) VALUES (1, 1, 'ABGEMELDET'); +INSERT INTO Vehicle (id, version, status) VALUES (2, 2, 'ABGEMELDET');
\ No newline at end of file diff --git a/src/main/resources/sql/database.sql b/src/main/resources/sql/database.sql index 9d1b0e1..4f3adf7 100644 --- a/src/main/resources/sql/database.sql +++ b/src/main/resources/sql/database.sql @@ -1,25 +1,30 @@  CREATE TABLE IF NOT EXISTS VehicleVersion (    id BIGINT AUTO_INCREMENT PRIMARY KEY,    name VARCHAR(100) NOT NULL, -  constructionType ENUM('Normal', 'Hochdach', 'Mittelhochdach') NOT NULL, -  type ENUM('BKTW', 'KTW-B', 'KTW', 'RTW', 'NEF', 'NAH') NOT NULL, +  constructionType VARCHAR NOT NULL, +  type VARCHAR NOT NULL, +  hasNef BOOLEAN NOT NULL, +  CHECK constructionType IN ('NORMAL', 'HOCHDACH', 'MITTELHOCHDACH'), +  CHECK type IN ('BKTW', 'KTW-B', 'KTW', 'RTW', 'NEF', 'NAH')  );  CREATE TABLE IF NOT EXISTS Vehicle (    id BIGINT AUTO_INCREMENT PRIMARY KEY,    version BIGINT NOT NULL, -  status ENUM('abgemeldet', 'frei_wache', 'zum_berufungsort', 'am_berufungsort', 'zum_zielort', -              'am_zielort', 'frei_funk', 'deleted') NOT NULL, +  status VARCHAR NOT NULL,    FOREIGN KEY (version) REFERENCES VehicleVersion(id), +  CHECK status IN ('ABGEMELDET', 'FREI_WACHE', 'ZUM_BERUFUNGSORT', 'AM_BERUFUNGSORT', 'ZUM_ZIELORT', +              'AM_ZIELORT', 'FREI_FUNK', 'DELETED')  );  CREATE TABLE IF NOT EXISTS EmployeeVersion (    id BIGINT AUTO_INCREMENT PRIMARY KEY,    name VARCHAR(100) NOT NULL,    birthday DATE NOT NULL, -  educationLevel ENUM('RS', 'NFS', 'NKV', 'NKA', 'NKI', 'NA') NOT NULL, +  educationLevel VARCHAR NOT NULL,    isDriver BOOLEAN NOT NULL,    isPilot BOOLEAN NOT NULL, +  CHECK educationLevel IN ('RS', 'NFS', 'NKV', 'NKA', 'NKI', 'NA')  );  CREATE TABLE IF NOT EXISTS Employee ( @@ -42,10 +47,13 @@ CREATE TABLE IF NOT EXISTS Registration (  CREATE TABLE IF NOT EXISTS Operation (    id BIGINT AUTO_INCREMENT PRIMARY KEY,    opCode VARCHAR(20) NOT NULL, -  severity ENUM('A', 'B', 'C', 'D', 'E', 'O') NOT NULL, +  severity VARCHAR NOT NULL,    created TIMESTAMP NOT NULL,    destination VARCHAR(100) NOT NULL,    additionalInfo VARCHAR(100), +  status VARCHAR NOT NULL, +  CHECK severity IN ('A', 'B', 'C', 'D', 'E', 'O'), +  CHECK status IN ('ACTIVE', 'COMPLETED', 'CANCELLED')  );  CREATE TABLE IF NOT EXISTS VehicleOperation ( diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/RegistrationWindowApplication.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/RegistrationWindowApplication.java new file mode 100644 index 0000000..3293ae9 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/controller/RegistrationWindowApplication.java @@ -0,0 +1,53 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.controller; + +import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader; +import java.lang.invoke.MethodHandles; +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; + +@Component +@ComponentScan("at.ac.tuwien.sepm.assignment.groupphase") +public class RegistrationWindowApplication extends Application { + +    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + +    public static AnnotationConfigApplicationContext context; + +    @Override +    public void start(Stage primaryStage) throws Exception { +        // setup application +        primaryStage.setTitle("Person anlegen"); +        // primaryStage.setWidth(1366); +        // primaryStage.setHeight(768); +        primaryStage.centerOnScreen(); +        primaryStage.setOnCloseRequest(event -> LOG.debug("Application shutdown initiated")); + +        context = new AnnotationConfigApplicationContext(RegistrationWindowApplication.class); +        final var fxmlLoader = context.getBean(SpringFXMLLoader.class); +        primaryStage.setScene( +                new Scene( +                        (Parent) +                                fxmlLoader.load( +                                        getClass() +                                                .getResourceAsStream( +                                                        "/fxml/RegistrationWindow.fxml")))); + +        // show application +        primaryStage.show(); +        primaryStage.toFront(); +        LOG.debug("Application startup complete"); +    } + +    @Override +    public void stop() { +        LOG.debug("Stopping application"); +        context.close(); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDatabaseDAOTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDatabaseDAOTest.java new file mode 100644 index 0000000..03059ff --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/dao/RegistrationDatabaseDAOTest.java @@ -0,0 +1,168 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao; + +import static org.junit.Assert.*; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registration; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager; +import java.nio.charset.Charset; +import java.sql.SQLException; +import java.time.Instant; +import java.time.LocalDate; +import java.util.LinkedList; +import java.util.List; +import org.h2.tools.RunScript; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class RegistrationDatabaseDAOTest { + +    // Base taken from EmployeePersistenceTest + +    private static final String JDBC_DRIVER = org.h2.Driver.class.getName(); +    private static final String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"; +    private static final String USER = ""; +    private static final String PASSWORD = ""; + +    private RegistrationDAO registrationDAO; + +    public RegistrationDatabaseDAOTest() throws PersistenceException { +        this.registrationDAO = new RegistrationDatabaseDAO(new JDBCConnectionManager(JDBC_URL)); +    } + +    @BeforeClass +    public static void setupDatabase() throws SQLException { +        RunScript.execute( +                JDBC_URL, +                USER, +                PASSWORD, +                "classpath:sql/database.sql", +                Charset.forName("UTF8"), +                false); +        RunScript.execute( +                JDBC_URL, +                USER, +                PASSWORD, +                "classpath:sql/H2RegistrationDAOTest_populate.sql", +                Charset.forName("UTF8"), +                false); +    } +    /* +        @Before +        public void setUp() throws SQLException { +            RunScript.execute( +                    JDBC_URL, +                    USER, +                    PASSWORD, +                    "classpath:sql/H2RegistrationDAOTest_populate.sql", +                    Charset.forName("UTF8"), +                    false); +        } +    */ +    @AfterClass +    public static void tearDown() throws SQLException { +        RunScript.execute( +                JDBC_URL, +                USER, +                PASSWORD, +                "classpath:sql/H2RegistrationDAOTest_depopulate.sql", +                Charset.forName("UTF8"), +                false); +    } + +    @Rule public ExpectedException thrown = ExpectedException.none(); + +    @Test +    public void addRegistrationsShouldSucceed() throws PersistenceException { +        List<Registration> registrations = new LinkedList<>(); +        /* +        Vehicle vehicle = Vehicle.builder() +                .id(1) +                .name("RTW-1") +                .constructionType(ConstructionType.HOCHDACH) +                .type(VehicleType.RTW) +                .status(Status.ABGEMELDET) +                .hasNef(true) +                .build(); +        */ +        Employee employee1 = +                Employee.builder() +                        .id(1) +                        .name("John Doe") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.RS) +                        .isDriver(true) +                        .isPilot(true) +                        .build(); +        Employee employee2 = +                Employee.builder() +                        .id(2) +                        .name("Nick \"Kage\" Verily") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.NKV) +                        .isDriver(true) +                        .isPilot(false) +                        .build(); +        Employee employee3 = +                Employee.builder() +                        .id(3) +                        .name("Nicht Arzt") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.NA) +                        .isDriver(false) +                        .isPilot(false) +                        .build(); +        Registration registration1 = +                Registration.builder() +                        .start(Instant.now()) // incorrect, but should be irrelevant to outcome +                        .end(Instant.now()) // same +                        .employee(employee1) +                        .build(); +        Registration registration2 = +                Registration.builder() +                        .start(Instant.now()) // incorrect, but should be irrelevant to outcome +                        .end(Instant.now()) // same +                        .employee(employee2) +                        .build(); +        Registration registration3 = +                Registration.builder() +                        .start(Instant.now()) // incorrect, but should be irrelevant to outcome +                        .end(Instant.now()) // same +                        .employee(employee3) +                        .build(); +        registrations.add(registration1); +        registrations.add(registration2); +        registrations.add(registration3); + +        List<Long> returnvalues = registrationDAO.add(1, registrations); +        assertFalse(returnvalues.isEmpty()); // can be improved... +    } + +    @Test +    public void addRegistrationToInexistentVehicleShouldFail() throws PersistenceException { +        thrown.expect(PersistenceException.class); +        List<Registration> registrations = new LinkedList<>(); +        Employee employee = +                Employee.builder() +                        .id(1) +                        .name("John Doe") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.RS) +                        .isDriver(true) +                        .isPilot(true) +                        .build(); +        Registration registration = +                Registration.builder() +                        .start(Instant.MIN) +                        .end(Instant.MAX) +                        .employee(employee) +                        .build(); +        registrations.add(registration); +        registrationDAO.add(200, registrations); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationServiceImplTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationServiceImplTest.java new file mode 100644 index 0000000..f3efbef --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/einsatzverwaltung/service/RegistrationServiceImplTest.java @@ -0,0 +1,148 @@ +package at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.RegistrationDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Registration; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.ConstructionType; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.Status; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.VehicleType; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidRegistrationException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class RegistrationServiceImplTest { + +    @Mock RegistrationDAO daoMock; + +    @Mock VehicleService vehicleService; + +    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + +    @Rule public ExpectedException thrown = ExpectedException.none(); + +    @Before +    public void setUp() throws ServiceException { +        MockitoAnnotations.initMocks(this); +        when(vehicleService.list(any())) +                .thenReturn( +                        Arrays.asList( +                                Vehicle.builder() +                                        .id(1) +                                        .name("RTW-1") +                                        .constructionType(ConstructionType.HOCHDACH) +                                        .status(Status.ABGEMELDET) +                                        .type(VehicleType.RTW) +                                        .hasNef(true) +                                        .build())); +    } + +    @Test +    public void addValidRegistrationsShouldSucceed() +            throws InvalidRegistrationException, ServiceException, InvalidVehicleException { +        RegistrationService registrationService = +                new RegistrationServiceImpl(daoMock, vehicleService); +        List<Registration> registrations = new LinkedList<>(); +        Vehicle vehicle = +                Vehicle.builder() +                        .id(1) +                        .name("RTW-1") +                        .constructionType(ConstructionType.HOCHDACH) +                        .type(VehicleType.RTW) +                        .status(Status.ABGEMELDET) +                        .hasNef(true) +                        .build(); +        Employee employee1 = +                Employee.builder() +                        .id(1) +                        .name("John Doe") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.RS) +                        .isDriver(true) +                        .isPilot(true) +                        .build(); +        Employee employee2 = +                Employee.builder() +                        .id(2) +                        .name("Nick \"Kage\" Verily") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.NKV) +                        .isDriver(true) +                        .isPilot(false) +                        .build(); +        Employee employee3 = +                Employee.builder() +                        .id(3) +                        .name("Nicht Arzt") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.NA) +                        .isDriver(false) +                        .isPilot(false) +                        .build(); +        Instant start = Instant.now(); +        Instant end = start.plus(8, ChronoUnit.HOURS); +        Registration registration1 = +                Registration.builder().start(start).end(end).employee(employee1).build(); +        Registration registration2 = +                Registration.builder().start(start).end(end).employee(employee2).build(); +        Registration registration3 = +                Registration.builder().start(start).end(end).employee(employee3).build(); +        registrations.add(registration1); +        registrations.add(registration2); +        registrations.add(registration3); +        registrationService.add(vehicle.id(), registrations); +    } + +    @Test +    public void addOnlyOnePersonToRTWShouldFail() +            throws InvalidRegistrationException, ServiceException, InvalidVehicleException { +        thrown.expect(InvalidRegistrationException.class); +        RegistrationService registrationService = +                new RegistrationServiceImpl(daoMock, vehicleService); +        List<Registration> registrations = new LinkedList<>(); +        Vehicle vehicle = +                Vehicle.builder() +                        .id(1) +                        .name("RTW-1") +                        .constructionType(ConstructionType.HOCHDACH) +                        .type(VehicleType.RTW) +                        .status(Status.ABGEMELDET) +                        .hasNef(true) +                        .build(); +        Employee employee = +                Employee.builder() +                        .id(1) +                        .name("John Doe") +                        .birthday(LocalDate.now()) // incorrect, but should be irrelevant +                        .educationLevel(EducationLevel.RS) +                        .isDriver(true) +                        .isPilot(true) +                        .build(); +        Registration registration = +                Registration.builder() +                        .start(Instant.MIN) +                        .end(Instant.MAX) +                        .employee(employee) +                        .build(); +        registrations.add(registration); +        registrationService.add(vehicle.id(), registrations); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/CreateNewEmployeeApplication.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/CreateNewEmployeeApplication.java new file mode 100644 index 0000000..e9f4801 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/CreateNewEmployeeApplication.java @@ -0,0 +1,53 @@ +package at.ac.tuwien.sepm.assignment.groupphase.employee; + +import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader; +import java.lang.invoke.MethodHandles; +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; + +@Component +@ComponentScan("at.ac.tuwien.sepm.assignment.groupphase") +public final class CreateNewEmployeeApplication extends Application { + +    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + +    public static AnnotationConfigApplicationContext context; + +    @Override +    public void start(Stage primaryStage) throws Exception { +        // setup application +        primaryStage.setTitle("Person anlegen"); +        primaryStage.setWidth(1366); +        primaryStage.setHeight(768); +        primaryStage.centerOnScreen(); +        primaryStage.setOnCloseRequest(event -> LOG.debug("Application shutdown initiated")); + +        context = new AnnotationConfigApplicationContext(CreateNewEmployeeApplication.class); +        final var fxmlLoader = context.getBean(SpringFXMLLoader.class); +        primaryStage.setScene( +                new Scene( +                        (Parent) +                                fxmlLoader.load( +                                        getClass() +                                                .getResourceAsStream( +                                                        "/fxml/createNewEmployee.fxml")))); + +        // show application +        primaryStage.show(); +        primaryStage.toFront(); +        LOG.debug("Application startup complete"); +    } + +    @Override +    public void stop() { +        LOG.debug("Stopping application"); +        context.close(); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/CreateNewEmployeeControllerTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/CreateNewEmployeeControllerTest.java new file mode 100644 index 0000000..eb1a728 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/CreateNewEmployeeControllerTest.java @@ -0,0 +1,85 @@ +package at.ac.tuwien.sepm.assignment.groupphase.employee; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.EmployeeService; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.Helper; +import at.ac.tuwien.sepm.assignment.groupphase.util.HighDpiAwareApplicationTest; +import javafx.scene.control.DialogPane; +import javafx.scene.input.MouseButton; +import javafx.stage.Stage; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.testfx.api.FxToolkit; +import org.testfx.robot.Motion; + +public class CreateNewEmployeeControllerTest extends HighDpiAwareApplicationTest { + +    private EmployeeService employeeService; + +    @Before +    public void setup() throws Exception { +        // TODO: check if testfx can be run in headless mode on Jenkins +        FxToolkit.registerPrimaryStage(); +        FxToolkit.setupApplication(CreateNewEmployeeApplication.class); +        employeeService = CreateNewEmployeeApplication.context.getBean(EmployeeService.class); +    } + +    @After +    public void cleanup() throws Exception { +        FxToolkit.cleanupStages(); +    } + +    @Test +    public void testClickAddValidEmployee() throws InvalidEmployeeException, ServiceException { + +        when(employeeService.add(any())).thenReturn(1L); + +        clickOn("#inputName", Motion.DIRECT, MouseButton.PRIMARY); +        write("Name"); +        clickOn("#btnCreate", Motion.DIRECT, MouseButton.PRIMARY); + +        Stage alertDialog = Helper.getTopModalStage(robotContext()); +        Assert.assertNotNull(alertDialog); + +        DialogPane dialogPane = (DialogPane) alertDialog.getScene().getRoot(); +        Assert.assertEquals("Erfolgreich angelegt", dialogPane.getHeaderText()); +    } + +    @Test +    public void testClickAddInvalidEmployee() throws InvalidEmployeeException, ServiceException { + +        when(employeeService.add(any())).thenThrow(InvalidEmployeeException.class); + +        moveTo("#inputName"); +        clickOn("#btnCreate", Motion.DIRECT, MouseButton.PRIMARY); + +        Stage alertDialog = Helper.getTopModalStage(robotContext()); +        Assert.assertNotNull(alertDialog); + +        DialogPane dialogPane = (DialogPane) alertDialog.getScene().getRoot(); +        Assert.assertEquals("Ungültige Eingabe", dialogPane.getHeaderText()); +    } + +    @Test +    public void testClickAddEmployeeWithServiceException() +            throws InvalidEmployeeException, ServiceException { + +        when(employeeService.add(any())).thenThrow(ServiceException.class); + +        clickOn("#inputName", Motion.DIRECT, MouseButton.PRIMARY); +        write("Test"); +        clickOn("#btnCreate", Motion.DIRECT, MouseButton.PRIMARY); + +        Stage alertDialog = Helper.getTopModalStage(robotContext()); +        Assert.assertNotNull(alertDialog); + +        DialogPane dialogPane = (DialogPane) alertDialog.getScene().getRoot(); +        Assert.assertEquals("Speicherfehler", dialogPane.getHeaderText()); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeePersistenceTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeePersistenceTest.java new file mode 100644 index 0000000..f8fe0f3 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeePersistenceTest.java @@ -0,0 +1,155 @@ +package at.ac.tuwien.sepm.assignment.groupphase.employee; + +import static junit.framework.TestCase.fail; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.EmployeeDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.EmployeeDatabaseDao; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.JDBCConnectionManager; +import java.nio.charset.Charset; +import java.sql.SQLException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import org.dbunit.IDatabaseTester; +import org.dbunit.JdbcDatabaseTester; +import org.dbunit.dataset.DataSetException; +import org.dbunit.dataset.IDataSet; +import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; +import org.dbunit.operation.DatabaseOperation; +import org.h2.tools.RunScript; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class EmployeePersistenceTest { + +    private static final String JDBC_DRIVER = org.h2.Driver.class.getName(); +    private static final String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"; +    private static final String USER = ""; +    private static final String PASSWORD = ""; + +    private EmployeeDAO employeePersistence; + +    public EmployeePersistenceTest() throws PersistenceException { +        employeePersistence = new EmployeeDatabaseDao(new JDBCConnectionManager(JDBC_URL)); +    } + +    @BeforeClass +    public static void createSchema() throws SQLException { +        RunScript.execute( +                JDBC_URL, +                USER, +                PASSWORD, +                "classpath:sql/database.sql", +                Charset.forName("UTF8"), +                false); +    } + +    @Before +    public void importDataSet() throws Exception { +        IDataSet dataSet = readDataSet(); +        cleanlyInsert(dataSet); +    } + +    private IDataSet readDataSet() throws DataSetException { +        return new FlatXmlDataSetBuilder() +                .build( +                        getClass() +                                .getClassLoader() +                                .getResourceAsStream("employeeServiceTestData.xml")); +    } + +    private void cleanlyInsert(IDataSet dataSet) throws Exception { +        IDatabaseTester databaseTester = +                new JdbcDatabaseTester(JDBC_DRIVER, JDBC_URL, USER, PASSWORD); + +        databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT); +        databaseTester.setDataSet(dataSet); +        databaseTester.onSetup(); +    } + +    @Test +    public void testListEmployees() { + +        try { +            List<Employee> employees = employeePersistence.list(); + +            Employee empOne = +                    Employee.builder() +                            .id(1) +                            .name("Adam") +                            .birthday( +                                    LocalDate.parse( +                                            "10.10.2010", +                                            DateTimeFormatter.ofPattern("dd.MM.yyyy"))) +                            .educationLevel(EducationLevel.RS) +                            .isDriver(true) +                            .isPilot(false) +                            .build(); + +            Employee empTwo = +                    Employee.builder() +                            .id(2) +                            .name("Max") +                            .birthday( +                                    LocalDate.parse( +                                            "11.11.1990", +                                            DateTimeFormatter.ofPattern("dd.MM.yyyy"))) +                            .educationLevel(EducationLevel.NFS) +                            .isDriver(false) +                            .isPilot(false) +                            .build(); + +            Employee empThree = +                    Employee.builder() +                            .id(3) +                            .name("Lisa") +                            .birthday( +                                    LocalDate.parse( +                                            "16.10.1999", +                                            DateTimeFormatter.ofPattern("dd.MM.yyyy"))) +                            .educationLevel(EducationLevel.NKI) +                            .isDriver(true) +                            .isPilot(false) +                            .build(); + +            Assert.assertTrue(employees.contains(empOne)); +            Assert.assertTrue(employees.contains(empTwo)); +            Assert.assertTrue(employees.contains(empThree)); +            Assert.assertEquals(3, employees.size()); + +        } catch (PersistenceException e) { +            fail(); +        } +    } + +    @Test +    public void testEmployeeListNoElement() { + +        try { +            List<Employee> employees = employeePersistence.list(); + +            Employee empOne = +                    Employee.builder() +                            .id(10) +                            .name("Adam") +                            .birthday( +                                    LocalDate.parse( +                                            "10.10.2010", +                                            DateTimeFormatter.ofPattern("dd.MM.yyyy"))) +                            .educationLevel(EducationLevel.RS) +                            .isDriver(true) +                            .isPilot(false) +                            .build(); + +            Assert.assertFalse(employees.contains(empOne)); + +        } catch (PersistenceException e) { +            fail(); +        } +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeeServiceTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeeServiceTest.java new file mode 100644 index 0000000..47328b3 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeeServiceTest.java @@ -0,0 +1,68 @@ +package at.ac.tuwien.sepm.assignment.groupphase.employee; + +import static junit.framework.TestCase.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.EmployeeDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.EmployeeDatabaseDao; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Employee.EducationLevel; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.EmployeeService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.EmployeeServiceImpl; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidEmployeeException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.time.LocalDate; +import org.junit.Assert; +import org.junit.Test; + +public class EmployeeServiceTest { + +    private final EmployeeDAO employeePersistence = mock(EmployeeDatabaseDao.class); +    private final EmployeeService employeeService = new EmployeeServiceImpl(employeePersistence); + +    public EmployeeServiceTest() throws PersistenceException { +        when(employeePersistence.add(any())).thenReturn(1L); +    } + +    @Test +    public void testAddValidEmployee() { + +        Employee employee = +                Employee.builder() +                        .name("Testperson") +                        .birthday(LocalDate.MIN) +                        .educationLevel(EducationLevel.NA) +                        .isDriver(true) +                        .isPilot(false) +                        .build(); + +        try { +            Assert.assertThat(employeeService.add(employee), is(1L)); +        } catch (InvalidEmployeeException | ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidEmployeeException.class) +    public void testAddInvalidEmployee() throws InvalidEmployeeException { + +        Employee employee = +                Employee.builder() +                        .name("") +                        .birthday(LocalDate.MIN) +                        .educationLevel(EducationLevel.NA) +                        .isDriver(true) +                        .isPilot(false) +                        .build(); + +        try { +            employeeService.add(employee); +        } catch (ServiceException e) { +            fail(); +        } +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeeServiceTestConfiguration.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeeServiceTestConfiguration.java new file mode 100644 index 0000000..3668ef4 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/employee/EmployeeServiceTestConfiguration.java @@ -0,0 +1,19 @@ +package at.ac.tuwien.sepm.assignment.groupphase.employee; + +import static org.mockito.Mockito.mock; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.EmployeeService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.EmployeeServiceImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +@Configuration +public class EmployeeServiceTestConfiguration { + +    @Bean +    @Primary +    public EmployeeService employeeService() { +        return mock(EmployeeServiceImpl.class); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationPersistenceTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationPersistenceTest.java new file mode 100644 index 0000000..be612d0 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationPersistenceTest.java @@ -0,0 +1,109 @@ +package at.ac.tuwien.sepm.assignment.groupphase.operation; + +public class OperationPersistenceTest { + +    /*private final OperationDAO operationDAO = +            new DBOperationDAO(new JDBCConnectionManager("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1")); + +    @BeforeClass +    public static void createSchema() throws SQLException { +        RunScript.execute( +                "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", +                "", +                "", +                "classpath:sql/database.sql", +                Charset.forName("UTF8"), +                false); +    } + +    @Test +    public void addOperationTest() { +        Vehicle vehicle = +                Vehicle.builder() +                        .status(Vehicle.Status.FREI_FUNK) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); + +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of(vehicle)) +                        .build(); +        try { +            operationDAO.add(operation); +        } catch (PersistenceException e) { +            fail(); +        } +    } + +    /*@Test(expected = PersistenceException.class) +    public void addFaultyOperationTest() throws PersistenceException { +        Vehicle vehicle = +                Vehicle.builder() +                        .status(Vehicle.Status.FREI_FUNK) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); + +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of(vehicle)) +                        .build(); +        operationDAO.add(operation); +    }*/ +    /* + +    @Test(expected = PersistenceException.class) +    public void addFaultyOperation1Test() throws PersistenceException { +        operationDAO.add(null); +    } + +    @Test(expected = PersistenceException.class) +    public void addFaultyOperation2Test() throws PersistenceException { +        Vehicle vehicle = +                Vehicle.builder() +                        .status(Vehicle.Status.FREI_FUNK) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); + +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination( +                                "Wiednerstraße 888, 1010 Wien Wiednerstraße 888, 1010 Wien Wiednerstraße 888, 1010 Wien Wiednerstraße 888, 1010 Wien Wiednerstraße 888, 1010 Wien ") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of(vehicle)) +                        .build(); +        operationDAO.add(operation); +    } + +    @Test(expected = PersistenceException.class) +    public void addConnectionTest() throws PersistenceException { +        operationDAO.connectVehicleToOperation(-1, 0); +    } + +    // TODO: ADD CONNECTION TESTS +    // KOMMT ID ZURÜCK?*/ +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationServiceComponentTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationServiceComponentTest.java new file mode 100644 index 0000000..286ee07 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationServiceComponentTest.java @@ -0,0 +1,130 @@ +package at.ac.tuwien.sepm.assignment.groupphase.operation; + +public class OperationServiceComponentTest { + +    /*private final OperationDAO operationDAO = +            new DBOperationDAO(new JDBCConnectionManager("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1")); +    private final OperationService operationService = new OperationServiceImpl(operationDAO); + +    @Test +    public void addOperationTest() { +        Vehicle vehicle = +                Vehicle.builder() +                        .status(Vehicle.Status.FREI_FUNK) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); + +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of(vehicle)) +                        .build(); +        try { +            // TODO: OPERATION DOES NOT WORK +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (InvalidOperationException | ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperationTest() throws InvalidOperationException { +        Vehicle vehicle = +                Vehicle.builder() +                        .status(Vehicle.Status.FREI_FUNK) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); +        Vehicle vehicle1 = +                Vehicle.builder() +                        .status(Vehicle.Status.ABGEMELDET) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); + +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of(vehicle, vehicle1)) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperation2Test() throws InvalidOperationException { +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of()) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            e.printStackTrace(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperation3Test() throws InvalidOperationException { +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of()) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            e.printStackTrace(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperation4Test() throws InvalidOperationException { +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("") +                        .created(Instant.now()) +                        .destination("Römergasse 7, 2500 Baden") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of()) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            e.printStackTrace(); +        } +    }*/ +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationServiceUnitTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationServiceUnitTest.java new file mode 100644 index 0000000..fc10553 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/operation/OperationServiceUnitTest.java @@ -0,0 +1,155 @@ +package at.ac.tuwien.sepm.assignment.groupphase.operation; + +import static junit.framework.TestCase.fail; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.OperationDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation.Severity; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Operation.Status; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.ConstructionType; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle.VehicleType; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.OperationService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.OperationServiceImpl; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidOperationException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import java.time.Instant; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; + +public class OperationServiceUnitTest { +    private final OperationDAO operationDAO = mock(OperationDAO.class); +    private final OperationService operationService = new OperationServiceImpl(operationDAO); + +    @Test +    public void addOperationTest() { +        try { +            when(operationDAO.add(any())).thenReturn(1L); +        } catch (PersistenceException e) { +            fail(); +        } +        Vehicle vehicle = +                Vehicle.builder() +                        .status(Vehicle.Status.FREI_FUNK) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); + +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of(vehicle)) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (InvalidOperationException | ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperationTest() throws InvalidOperationException { +        Vehicle vehicle = +                Vehicle.builder() +                        .status(Vehicle.Status.FREI_FUNK) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); +        Vehicle vehicle1 = +                Vehicle.builder() +                        .status(Vehicle.Status.ABGEMELDET) +                        .constructionType(ConstructionType.HOCHDACH) +                        .name("BKTW_123") +                        .hasNef(true) +                        .type(VehicleType.BKTW) +                        .build(); + +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of(vehicle, vehicle1)) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperation2Test() throws InvalidOperationException { +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("Wiedner Hauptstraße 35, Wien") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of()) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            e.printStackTrace(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperation3Test() throws InvalidOperationException { +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("ALP-95E7") +                        .created(Instant.now()) +                        .destination("") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of()) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            e.printStackTrace(); +        } +    } + +    @Test(expected = InvalidOperationException.class) +    public void addFaultyOperation4Test() throws InvalidOperationException { +        Operation operation = +                Operation.builder() +                        .status(Status.ACTIVE) +                        .opCode("") +                        .created(Instant.now()) +                        .destination("Römergasse 7, 2500 Baden") +                        .additionalInfo("HTU Wien") +                        .severity(Severity.B) +                        .vehicles(List.of()) +                        .build(); +        try { +            Assert.assertThat(operationService.add(operation), is(1L)); +        } catch (ServiceException e) { +            e.printStackTrace(); +        } +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/Helper.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/Helper.java new file mode 100644 index 0000000..b808206 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/Helper.java @@ -0,0 +1,28 @@ +package at.ac.tuwien.sepm.assignment.groupphase.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.Window; +import org.testfx.api.FxRobotContext; + +public class Helper { + +    public static Stage getTopModalStage(FxRobotContext robotContext) { + +        List<Window> allWindows = new ArrayList<>(robotContext.getWindowFinder().listWindows()); +        Collections.reverse(allWindows); +        return (Stage) +                allWindows +                        .stream() +                        .filter(window -> window instanceof Stage) +                        .filter( +                                window -> +                                        ((Stage) window).getModality() +                                                == Modality.APPLICATION_MODAL) +                        .findFirst() +                        .orElse(null); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/HighDpiAwareApplicationTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/HighDpiAwareApplicationTest.java new file mode 100644 index 0000000..c9816a1 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/HighDpiAwareApplicationTest.java @@ -0,0 +1,24 @@ +package at.ac.tuwien.sepm.assignment.groupphase.util; + +import javafx.geometry.Bounds; +import javafx.scene.Node; +import org.testfx.api.FxRobotContext; +import org.testfx.framework.junit.ApplicationTest; +import org.testfx.service.locator.impl.BoundsLocatorImpl; +import org.testfx.service.locator.impl.PointLocatorImpl; + +public class HighDpiAwareApplicationTest extends ApplicationTest { + +    public HighDpiAwareApplicationTest() { +        FxRobotContext context = robotContext(); +        context.setBoundsLocator( +                new BoundsLocatorImpl() { +                    @Override +                    public Bounds boundsOnScreenFor(Node node) { +                        Bounds bounds = super.boundsOnScreenFor(node); +                        return ScaledBounds.wrap(bounds); +                    } +                }); +        robotContext().setPointLocator(new PointLocatorImpl(context.getBoundsLocator())); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/ScaledBounds.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/ScaledBounds.java new file mode 100644 index 0000000..02c15c4 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/util/ScaledBounds.java @@ -0,0 +1,34 @@ +package at.ac.tuwien.sepm.assignment.groupphase.util; + +import java.awt.GraphicsEnvironment; +import javafx.geometry.BoundingBox; +import javafx.geometry.Bounds; + +public class ScaledBounds extends BoundingBox { + +    private static final double scale; + +    static { +        scale = +                1 +                        / GraphicsEnvironment.getLocalGraphicsEnvironment() +                                .getDefaultScreenDevice() +                                .getDefaultConfiguration() +                                .getDefaultTransform() +                                .getScaleX(); +    } + +    public static ScaledBounds wrap(Bounds bounds) { +        return new ScaledBounds(bounds); +    } + +    private ScaledBounds(Bounds wrapped) { +        super( +                wrapped.getMinX() * scale, +                wrapped.getMinY() * scale, +                wrapped.getMinZ() * scale, +                wrapped.getWidth() * scale, +                wrapped.getHeight() * scale, +                wrapped.getDepth()); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CarAddTestService.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CarAddTestService.java new file mode 100644 index 0000000..de7a26a --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CarAddTestService.java @@ -0,0 +1,283 @@ +package at.ac.tuwien.sepm.assignment.groupphase.vehicle; + +import static junit.framework.TestCase.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.VehicleDAO; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dao.VehicleDatabaseDao; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.dto.Vehicle; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleServiceImpl; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.PersistenceException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import org.junit.Test; + +public class CarAddTestService { +    private final VehicleDAO vehicleP = mock(VehicleDatabaseDao.class); +    private final VehicleService vehicleService = new VehicleServiceImpl(vehicleP); + +    public CarAddTestService() throws PersistenceException { +        when(vehicleP.add(any())).thenReturn(1L); +    } + +    @Test +    public void testValidVehicleH() { +        Vehicle vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.HOCHDACH) +                        .type(Vehicle.VehicleType.RTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.HOCHDACH) +                        .type(Vehicle.VehicleType.KTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.HOCHDACH) +                        .type(Vehicle.VehicleType.KTW_B) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.HOCHDACH) +                        .type(Vehicle.VehicleType.BKTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +    } + +    @Test +    public void testValidVehicleM() { +        Vehicle vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.MITTELHOCHDACH) +                        .type(Vehicle.VehicleType.KTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.MITTELHOCHDACH) +                        .type(Vehicle.VehicleType.KTW_B) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.MITTELHOCHDACH) +                        .type(Vehicle.VehicleType.BKTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +    } + +    @Test +    public void testValidVehicleN() { +        Vehicle vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.NORMAL) +                        .type(Vehicle.VehicleType.BKTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.NORMAL) +                        .type(Vehicle.VehicleType.NEF) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.NORMAL) +                        .type(Vehicle.VehicleType.NAH) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (InvalidVehicleException | ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidVehicleException.class) +    public void testInvalidVehicleH() throws InvalidVehicleException { +        Vehicle vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.HOCHDACH) +                        .type(Vehicle.VehicleType.NEF) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.HOCHDACH) +                        .type(Vehicle.VehicleType.NAH) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidVehicleException.class) +    public void testInvalidVehicleM() throws InvalidVehicleException { +        Vehicle vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.MITTELHOCHDACH) +                        .type(Vehicle.VehicleType.NEF) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.MITTELHOCHDACH) +                        .type(Vehicle.VehicleType.NAH) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.MITTELHOCHDACH) +                        .type(Vehicle.VehicleType.RTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +    } + +    @Test(expected = InvalidVehicleException.class) +    public void testInvalidVehicleN() throws InvalidVehicleException { +        Vehicle vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.NORMAL) +                        .type(Vehicle.VehicleType.RTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.NORMAL) +                        .type(Vehicle.VehicleType.KTW_B) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +        vehicle = +                Vehicle.builder() +                        .constructionType(Vehicle.ConstructionType.NORMAL) +                        .type(Vehicle.VehicleType.KTW) +                        .hasNef(true) +                        .status(Vehicle.Status.ABGEMELDET) +                        .name("") +                        .build(); +        try { +            vehicleService.add(vehicle); +        } catch (ServiceException e) { +            fail(); +        } +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CreateNewVehicleApplication.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CreateNewVehicleApplication.java new file mode 100644 index 0000000..dcd88ec --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CreateNewVehicleApplication.java @@ -0,0 +1,51 @@ +package at.ac.tuwien.sepm.assignment.groupphase.vehicle; + +import at.ac.tuwien.sepm.assignment.groupphase.util.SpringFXMLLoader; +import java.lang.invoke.MethodHandles; +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; + +@Component +@ComponentScan("at.ac.tuwien.sepm.assignment.groupphase") +public class CreateNewVehicleApplication extends Application { + +    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + +    public static AnnotationConfigApplicationContext context; + +    @Override +    public void start(Stage primaryStage) throws Exception { +        // setup application +        primaryStage.setTitle("Fahrzeug anlegen"); +        primaryStage.setWidth(1366); +        primaryStage.setHeight(768); +        primaryStage.centerOnScreen(); +        primaryStage.setOnCloseRequest(event -> LOG.debug("Application shutdown initiated")); + +        context = new AnnotationConfigApplicationContext(CreateNewVehicleApplication.class); +        final var fxmlLoader = context.getBean(SpringFXMLLoader.class); +        primaryStage.setScene( +                new Scene( +                        (Parent) +                                fxmlLoader.load( +                                        getClass().getResourceAsStream("/fxml/createCar.fxml")))); + +        // show application +        primaryStage.show(); +        primaryStage.toFront(); +        LOG.debug("Application startup complete"); +    } + +    @Override +    public void stop() { +        LOG.debug("Stopping application"); +        context.close(); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CreateNewVehicleControllerTest.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CreateNewVehicleControllerTest.java new file mode 100644 index 0000000..866ed74 --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/CreateNewVehicleControllerTest.java @@ -0,0 +1,79 @@ +package at.ac.tuwien.sepm.assignment.groupphase.vehicle; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleService; +import at.ac.tuwien.sepm.assignment.groupphase.exception.InvalidVehicleException; +import at.ac.tuwien.sepm.assignment.groupphase.exception.ServiceException; +import at.ac.tuwien.sepm.assignment.groupphase.util.Helper; +import at.ac.tuwien.sepm.assignment.groupphase.util.HighDpiAwareApplicationTest; +import javafx.scene.control.DialogPane; +import javafx.scene.input.MouseButton; +import javafx.stage.Stage; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.testfx.api.FxToolkit; +import org.testfx.robot.Motion; + +public class CreateNewVehicleControllerTest extends HighDpiAwareApplicationTest { + +    private VehicleService vehicleService; + +    @Before +    public void setup() throws Exception { +        // TODO: check if testfx can be run in headless mode on Jenkins +        FxToolkit.registerPrimaryStage(); +        FxToolkit.setupApplication(CreateNewVehicleApplication.class); +        vehicleService = CreateNewVehicleApplication.context.getBean(VehicleService.class); +    } + +    @After +    public void cleanup() throws Exception { +        FxToolkit.cleanupStages(); +    } + +    @Test +    public void testClickAddValidVehicle() throws ServiceException, InvalidVehicleException { + +        when(vehicleService.add(any())).thenReturn(1L); + +        clickOn("#btn_create", Motion.DIRECT, MouseButton.PRIMARY); + +        Stage alertDialog = Helper.getTopModalStage(robotContext()); +        Assert.assertNotNull(alertDialog); + +        DialogPane dialogPane = (DialogPane) alertDialog.getScene().getRoot(); +        Assert.assertEquals("Speichern Erfolgreich", dialogPane.getHeaderText()); +    } + +    @Test +    public void testClickInvalidVehicleEx() throws ServiceException, InvalidVehicleException { + +        when(vehicleService.add(any())).thenThrow(InvalidVehicleException.class); + +        clickOn("#btn_create", Motion.DIRECT, MouseButton.PRIMARY); + +        Stage alertDialog = Helper.getTopModalStage(robotContext()); +        Assert.assertNotNull(alertDialog); + +        DialogPane dialogPane = (DialogPane) alertDialog.getScene().getRoot(); +        Assert.assertEquals("Ungültige Eingabe", dialogPane.getHeaderText()); +    } + +    @Test +    public void testClickInvalidServiceEx() throws ServiceException, InvalidVehicleException { + +        when(vehicleService.add(any())).thenThrow(ServiceException.class); + +        clickOn("#btn_create", Motion.DIRECT, MouseButton.PRIMARY); + +        Stage alertDialog = Helper.getTopModalStage(robotContext()); +        Assert.assertNotNull(alertDialog); + +        DialogPane dialogPane = (DialogPane) alertDialog.getScene().getRoot(); +        Assert.assertEquals("Fehler", dialogPane.getHeaderText()); +    } +} diff --git a/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/VehicleServiceTestConfiguration.java b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/VehicleServiceTestConfiguration.java new file mode 100644 index 0000000..ccd1e5d --- /dev/null +++ b/src/test/java/at/ac/tuwien/sepm/assignment/groupphase/vehicle/VehicleServiceTestConfiguration.java @@ -0,0 +1,19 @@ +package at.ac.tuwien.sepm.assignment.groupphase.vehicle; + +import static org.mockito.Mockito.mock; + +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleService; +import at.ac.tuwien.sepm.assignment.groupphase.einsatzverwaltung.service.VehicleServiceImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +@Configuration +public class VehicleServiceTestConfiguration { + +    @Bean +    @Primary +    public VehicleService vehicleService() { +        return mock(VehicleServiceImpl.class); +    } +} diff --git a/src/test/resources/employeeServiceTestData.xml b/src/test/resources/employeeServiceTestData.xml new file mode 100644 index 0000000..c21fde6 --- /dev/null +++ b/src/test/resources/employeeServiceTestData.xml @@ -0,0 +1,12 @@ +<dataset> +  <EmployeeVersion id="10" name="Adam" birthday="2010-10-10" educationlevel="RS" isDriver="true" +    isPilot="false"/> +  <EmployeeVersion id="20" name="Max" birthday="1990-11-11" educationlevel="NFS" isDriver="false" +    isPilot="false"/> +  <EmployeeVersion id="30" name="Lisa" birthday="1999-10-16" educationlevel="NKI" isDriver="true" +    isPilot="false"/> + +  <Employee id="1" version="10" /> +  <Employee id="2" version="20" /> +  <Employee id="3" version="30" /> +</dataset>
\ No newline at end of file  | 
