Search in sources :

Example 1 with PersonImportSimilarityResult

use of de.symeda.sormas.ui.importer.PersonImportSimilarityResult in project SORMAS-Project by hzi-braunschweig.

the class EventImporter method handlePersonSimilarity.

/**
 * Presents a popup window to the user that allows them to deal with detected potentially duplicate persons.
 * By passing the desired result to the resultConsumer, the importer decided how to proceed with the import process.
 */
protected void handlePersonSimilarity(PersonDto newPerson, Consumer<PersonImportSimilarityResult> resultConsumer) {
    currentUI.accessSynchronously(() -> {
        PersonSelectionField personSelect = new PersonSelectionField(newPerson, I18nProperties.getString(Strings.infoSelectOrCreatePersonForImport));
        personSelect.setWidth(1024, Unit.PIXELS);
        if (personSelect.hasMatches()) {
            final CommitDiscardWrapperComponent<PersonSelectionField> component = new CommitDiscardWrapperComponent<>(personSelect);
            component.addCommitListener(() -> {
                SimilarPersonDto person = personSelect.getValue();
                if (person == null) {
                    resultConsumer.accept(new PersonImportSimilarityResult(null, ImportSimilarityResultOption.CREATE));
                } else {
                    resultConsumer.accept(new PersonImportSimilarityResult(person, ImportSimilarityResultOption.PICK));
                }
            });
            component.addDiscardListener(() -> resultConsumer.accept(new PersonImportSimilarityResult(null, ImportSimilarityResultOption.SKIP)));
            personSelect.setSelectionChangeCallback((commitAllowed) -> component.getCommitButton().setEnabled(commitAllowed));
            Window window = VaadinUiUtil.showModalPopupWindow(component, I18nProperties.getString(Strings.headingPickOrCreatePerson));
            window.addCloseListener(event -> resultConsumer.accept(new PersonImportSimilarityResult(null, ImportSimilarityResultOption.SKIP)));
            personSelect.selectBestMatch();
        } else {
            resultConsumer.accept(new PersonImportSimilarityResult(null, ImportSimilarityResultOption.CREATE));
        }
    });
}
Also used : Window(com.vaadin.ui.Window) PersonSelectionField(de.symeda.sormas.ui.person.PersonSelectionField) CommitDiscardWrapperComponent(de.symeda.sormas.ui.utils.CommitDiscardWrapperComponent) PersonImportSimilarityResult(de.symeda.sormas.ui.importer.PersonImportSimilarityResult) SimilarPersonDto(de.symeda.sormas.api.person.SimilarPersonDto)

Example 2 with PersonImportSimilarityResult

use of de.symeda.sormas.ui.importer.PersonImportSimilarityResult in project SORMAS-Project by hzi-braunschweig.

the class EventParticipantImporterTest method testImportEventParticipantSimilarityPick.

@Test
public void testImportEventParticipantSimilarityPick() throws IOException, InvalidColumnException, InterruptedException, CsvValidationException, URISyntaxException {
    EventParticipantFacadeEjbLocal eventParticipantFacade = getBean(EventParticipantFacadeEjbLocal.class);
    RDCF rdcf = creator.createRDCF("Region", "District", "Community", "Facility");
    UserDto user = creator.createUser(rdcf.region.getUuid(), rdcf.district.getUuid(), rdcf.facility.getUuid(), "Surv", "Sup", UserRole.SURVEILLANCE_SUPERVISOR);
    EventDto event = creator.createEvent(EventStatus.SIGNAL, "Title", "Description", "First", "Name", "12345", TypeOfPlace.PUBLIC_PLACE, DateHelper.subtractDays(new Date(), 2), new Date(), user.toReference(), user.toReference(), Disease.EVD);
    EventReferenceDto eventRef = event.toReference();
    PersonDto person = creator.createPerson("Günther", "Heinz");
    creator.createCase(user.toReference(), person.toReference(), Disease.EVD, CaseClassification.PROBABLE, InvestigationStatus.PENDING, new Date(), rdcf);
    // Person Similarity: pick
    List<SimilarPersonDto> persons = FacadeProvider.getPersonFacade().getSimilarPersonDtos(new PersonSimilarityCriteria());
    File csvFile = new File(getClass().getClassLoader().getResource("sormas_eventparticipant_import_test_similarities.csv").toURI());
    EventParticipantImporterExtension eventParticipantImporter = new EventParticipantImporterExtension(csvFile, user, event) {

        @Override
        protected <T extends PersonImportSimilarityResult> void handlePersonSimilarity(PersonDto newPerson, Consumer<T> resultConsumer, BiFunction<SimilarPersonDto, ImportSimilarityResultOption, T> createSimilarityResult, String infoText, UI currentUI) {
            List<SimilarPersonDto> entries = new ArrayList<>();
            for (SimilarPersonDto person : persons) {
                if (PersonHelper.areNamesSimilar(newPerson.getFirstName(), newPerson.getLastName(), person.getFirstName(), person.getLastName(), null)) {
                    entries.add(person);
                }
            }
            resultConsumer.accept((T) new PersonImportSimilarityResult(entries.get(0), ImportSimilarityResultOption.PICK));
        }
    };
    ImportResultStatus importResult = eventParticipantImporter.runImport();
    EventParticipantIndexDto importedEventParticipant = eventParticipantFacade.getIndexList(new EventParticipantCriteria().withEvent(eventRef), null, null, null).get(0);
    PersonDto importedPerson = getPersonFacade().getPersonByUuid(importedEventParticipant.getPersonUuid());
    assertEquals(ImportResultStatus.COMPLETED, importResult);
    assertEquals(1, eventParticipantFacade.count(new EventParticipantCriteria().withEvent(eventRef)));
    assertEquals(person.getUuid(), importedEventParticipant.getPersonUuid());
    assertEquals(person.getFirstName(), importedPerson.getFirstName());
    assertEquals(person.getLastName(), importedPerson.getLastName());
    assertEquals(1, getPersonFacade().getAllUuids().size());
}
Also used : PersonDto(de.symeda.sormas.api.person.PersonDto) SimilarPersonDto(de.symeda.sormas.api.person.SimilarPersonDto) UserDto(de.symeda.sormas.api.user.UserDto) EventDto(de.symeda.sormas.api.event.EventDto) ArrayList(java.util.ArrayList) Date(java.util.Date) PersonSimilarityCriteria(de.symeda.sormas.api.person.PersonSimilarityCriteria) SimilarPersonDto(de.symeda.sormas.api.person.SimilarPersonDto) RDCF(de.symeda.sormas.ui.TestDataCreator.RDCF) Consumer(java.util.function.Consumer) UI(com.vaadin.ui.UI) BiFunction(java.util.function.BiFunction) EventReferenceDto(de.symeda.sormas.api.event.EventReferenceDto) EventParticipantFacadeEjbLocal(de.symeda.sormas.backend.event.EventParticipantFacadeEjb.EventParticipantFacadeEjbLocal) ImportResultStatus(de.symeda.sormas.ui.importer.ImportResultStatus) EventParticipantIndexDto(de.symeda.sormas.api.event.EventParticipantIndexDto) EventParticipantCriteria(de.symeda.sormas.api.event.EventParticipantCriteria) File(java.io.File) PersonImportSimilarityResult(de.symeda.sormas.ui.importer.PersonImportSimilarityResult) AbstractBeanTest(de.symeda.sormas.ui.AbstractBeanTest) Test(org.junit.Test)

Example 3 with PersonImportSimilarityResult

use of de.symeda.sormas.ui.importer.PersonImportSimilarityResult in project SORMAS-Project by hzi-braunschweig.

the class EventParticipantImporterTest method testImportEventParticipantSimilarityPickEventParticipant.

@Test
public void testImportEventParticipantSimilarityPickEventParticipant() throws IOException, InvalidColumnException, InterruptedException, CsvValidationException, URISyntaxException {
    EventParticipantFacadeEjbLocal eventParticipantFacade = getBean(EventParticipantFacadeEjbLocal.class);
    RDCF rdcf = creator.createRDCF("Region", "District", "Community", "Facility");
    UserDto user = creator.createUser(rdcf.region.getUuid(), rdcf.district.getUuid(), rdcf.facility.getUuid(), "Surv", "Sup", UserRole.SURVEILLANCE_SUPERVISOR);
    EventDto event = creator.createEvent(EventStatus.SIGNAL, "Title", "Description", "First", "Name", "12345", TypeOfPlace.PUBLIC_PLACE, DateHelper.subtractDays(new Date(), 2), new Date(), user.toReference(), user.toReference(), Disease.EVD);
    EventReferenceDto eventRef = event.toReference();
    PersonDto person = creator.createPerson("Günther", "Heinz");
    EventParticipantDto eventParticipant = creator.createEventParticipant(eventRef, person, "old desc", user.toReference());
    // Person Similarity: pick event participant
    List<SimilarPersonDto> persons = FacadeProvider.getPersonFacade().getSimilarPersonDtos(new PersonSimilarityCriteria());
    File csvFile = new File(getClass().getClassLoader().getResource("sormas_eventparticipant_import_test_similarities.csv").toURI());
    EventParticipantImporterExtension eventParticipantImporter = new EventParticipantImporterExtension(csvFile, user, event) {

        @Override
        protected <T extends PersonImportSimilarityResult> void handlePersonSimilarity(PersonDto newPerson, Consumer<T> resultConsumer, BiFunction<SimilarPersonDto, ImportSimilarityResultOption, T> createSimilarityResult, String infoText, UI currentUI) {
            List<SimilarPersonDto> entries = new ArrayList<>();
            for (SimilarPersonDto person : persons) {
                if (PersonHelper.areNamesSimilar(newPerson.getFirstName(), newPerson.getLastName(), person.getFirstName(), person.getLastName(), null)) {
                    entries.add(person);
                }
            }
            resultConsumer.accept((T) new PersonImportSimilarityResult(entries.get(0), ImportSimilarityResultOption.PICK));
        }
    };
    ImportResultStatus importResult = eventParticipantImporter.runImport();
    EventParticipantIndexDto importedEventParticipant = eventParticipantFacade.getIndexList(new EventParticipantCriteria().withEvent(eventRef), null, null, null).get(0);
    assertEquals(ImportResultStatus.COMPLETED, importResult);
    assertEquals(1, eventParticipantFacade.count(new EventParticipantCriteria().withEvent(eventRef)));
    assertEquals(person.getUuid(), importedEventParticipant.getPersonUuid());
    assertEquals(eventParticipant.getUuid(), importedEventParticipant.getUuid());
    assertEquals("description 1", importedEventParticipant.getInvolvementDescription());
    assertEquals(1, getPersonFacade().getAllUuids().size());
}
Also used : PersonDto(de.symeda.sormas.api.person.PersonDto) SimilarPersonDto(de.symeda.sormas.api.person.SimilarPersonDto) UserDto(de.symeda.sormas.api.user.UserDto) EventDto(de.symeda.sormas.api.event.EventDto) ArrayList(java.util.ArrayList) EventParticipantDto(de.symeda.sormas.api.event.EventParticipantDto) Date(java.util.Date) PersonSimilarityCriteria(de.symeda.sormas.api.person.PersonSimilarityCriteria) SimilarPersonDto(de.symeda.sormas.api.person.SimilarPersonDto) RDCF(de.symeda.sormas.ui.TestDataCreator.RDCF) Consumer(java.util.function.Consumer) UI(com.vaadin.ui.UI) BiFunction(java.util.function.BiFunction) EventReferenceDto(de.symeda.sormas.api.event.EventReferenceDto) EventParticipantFacadeEjbLocal(de.symeda.sormas.backend.event.EventParticipantFacadeEjb.EventParticipantFacadeEjbLocal) ImportResultStatus(de.symeda.sormas.ui.importer.ImportResultStatus) EventParticipantIndexDto(de.symeda.sormas.api.event.EventParticipantIndexDto) EventParticipantCriteria(de.symeda.sormas.api.event.EventParticipantCriteria) File(java.io.File) PersonImportSimilarityResult(de.symeda.sormas.ui.importer.PersonImportSimilarityResult) AbstractBeanTest(de.symeda.sormas.ui.AbstractBeanTest) Test(org.junit.Test)

Example 4 with PersonImportSimilarityResult

use of de.symeda.sormas.ui.importer.PersonImportSimilarityResult in project SORMAS-Project by hzi-braunschweig.

the class EventParticipantImporter method importDataFromCsvLine.

@Override
protected ImportLineResult importDataFromCsvLine(String[] values, String[] entityClasses, String[] entityProperties, String[][] entityPropertyPaths, boolean firstLine) throws IOException, InterruptedException {
    // Check whether the new line has the same length as the header line
    if (values.length > entityProperties.length) {
        writeImportError(values, I18nProperties.getValidationError(Validations.importLineTooLong));
        return ImportLineResult.ERROR;
    }
    // regenerate the UUID to prevent overwrite in case of export and import of the same entities
    int uuidIndex = ArrayUtils.indexOf(entityProperties, EventParticipantDto.UUID);
    if (uuidIndex >= 0) {
        values[uuidIndex] = DataHelper.createUuid();
    }
    int personUuidIndex = ArrayUtils.indexOf(entityProperties, String.join(".", EventParticipantDto.PERSON, PersonDto.UUID));
    if (personUuidIndex >= 0) {
        values[personUuidIndex] = DataHelper.createUuid();
    }
    final PersonDto newPersonTemp = PersonDto.buildImportEntity();
    final EventParticipantDto newEventParticipantTemp = EventParticipantDto.build(event.toReference(), currentUser.toReference());
    newEventParticipantTemp.setPerson(newPersonTemp);
    final List<VaccinationDto> vaccinations = new ArrayList<>();
    ImportRelatedObjectsMapper.Builder relatedObjectsMapperBuilder = new ImportRelatedObjectsMapper.Builder();
    if (FacadeProvider.getFeatureConfigurationFacade().isPropertyValueTrue(FeatureType.IMMUNIZATION_MANAGEMENT, FeatureTypeProperty.REDUCED) && event.getDisease() != null) {
        relatedObjectsMapperBuilder.addMapper(VaccinationDto.class, vaccinations, () -> VaccinationDto.build(currentUser.toReference()), this::insertColumnEntryIntoRelatedObject);
    }
    ImportRelatedObjectsMapper relatedMapper = relatedObjectsMapperBuilder.build();
    boolean eventParticipantHasImportError = insertRowIntoData(values, entityClasses, entityPropertyPaths, true, importColumnInformation -> {
        try {
            if (!relatedMapper.map(importColumnInformation)) {
                // If the cell entry is not empty, try to insert it into the current contact or person object
                if (!StringUtils.isEmpty(importColumnInformation.getValue())) {
                    insertColumnEntryIntoData(newEventParticipantTemp, newPersonTemp, importColumnInformation.getValue(), importColumnInformation.getEntityPropertyPath());
                }
            }
        } catch (ImportErrorException | InvalidColumnException e) {
            return e;
        }
        return null;
    });
    // If the row does not have any import errors, call the backend validation of all associated entities
    if (!eventParticipantHasImportError) {
        try {
            personFacade.validate(newPersonTemp);
            eventParticipantFacade.validate(newEventParticipantTemp);
        } catch (ValidationRuntimeException e) {
            eventParticipantHasImportError = true;
            writeImportError(values, e.getMessage());
        }
    }
    PersonDto newPerson = newPersonTemp;
    // Sanitize non-HOME address
    PersonHelper.sanitizeNonHomeAddress(newPerson);
    // if there are any, display a window to resolve the conflict to the user
    if (!eventParticipantHasImportError) {
        EventParticipantDto newEventParticipant = newEventParticipantTemp;
        try {
            EventParticipantImportConsumer consumer = new EventParticipantImportConsumer();
            ImportSimilarityResultOption resultOption = null;
            EventParticipantImportLock personSelectLock = new EventParticipantImportLock();
            // We need to pause the current thread to prevent the import from continuing until the user has acted
            synchronized (personSelectLock) {
                // Call the logic that allows the user to handle the similarity; once this has been done, the LOCK should be notified
                // to allow the importer to resume
                handlePersonSimilarity(newPerson, result -> consumer.onImportResult(result, personSelectLock), (person, similarityResultOption) -> new PersonImportSimilarityResult(person, similarityResultOption), Strings.infoSelectOrCreatePersonForImport, currentUI);
                try {
                    if (!personSelectLock.wasNotified) {
                        personSelectLock.wait();
                    }
                } catch (InterruptedException e) {
                    logger.error("InterruptedException when trying to perform LOCK.wait() in eventparticipant import: " + e.getMessage());
                    throw e;
                }
                if (consumer.result != null) {
                    resultOption = consumer.result.getResultOption();
                }
                // If the user picked an existing person, override the eventparticipant person with it
                if (ImportSimilarityResultOption.PICK.equals(resultOption)) {
                    newPerson = personFacade.getPersonByUuid(consumer.result.getMatchingPerson().getUuid());
                    // get first eventparticipant for event and person
                    EventParticipantCriteria eventParticipantCriteria = new EventParticipantCriteria().withPerson(newPerson.toReference()).withEvent(event.toReference());
                    EventParticipantDto pickedEventParticipant = eventParticipantFacade.getFirst(eventParticipantCriteria);
                    if (pickedEventParticipant != null) {
                        // re-apply import on pickedEventParticipant
                        insertRowIntoData(values, entityClasses, entityPropertyPaths, true, importColumnInformation -> {
                            // If the cell entry is not empty, try to insert it into the current contact or person object
                            if (!StringUtils.isEmpty(importColumnInformation.getValue())) {
                                try {
                                    insertColumnEntryIntoData(pickedEventParticipant, newPersonTemp, importColumnInformation.getValue(), importColumnInformation.getEntityPropertyPath());
                                } catch (ImportErrorException | InvalidColumnException e) {
                                    return e;
                                }
                            }
                            return null;
                        });
                        newEventParticipant = pickedEventParticipant;
                    }
                }
            }
            // or an existing person was picked, save the eventparticipant and person to the database
            if (ImportSimilarityResultOption.SKIP.equals(resultOption)) {
                return ImportLineResult.SKIPPED;
            } else {
                // Workaround: Reset the change date to avoid OutdatedEntityExceptions
                newPerson.setChangeDate(new Date());
                boolean skipPersonValidation = ImportSimilarityResultOption.PICK.equals(resultOption);
                final PersonDto savedPerson = personFacade.savePerson(newPerson, skipPersonValidation);
                newEventParticipant.setPerson(savedPerson);
                newEventParticipant.setChangeDate(new Date());
                eventParticipantFacade.saveEventParticipant(newEventParticipant);
                for (VaccinationDto vaccination : vaccinations) {
                    FacadeProvider.getVaccinationFacade().createWithImmunization(vaccination, newEventParticipant.getRegion(), newEventParticipant.getDistrict(), newEventParticipant.getPerson().toReference(), event.getDisease());
                }
                consumer.result = null;
                return ImportLineResult.SUCCESS;
            }
        } catch (ValidationRuntimeException e) {
            writeImportError(values, e.getMessage());
            return ImportLineResult.ERROR;
        }
    } else {
        return ImportLineResult.ERROR;
    }
}
Also used : ImportErrorException(de.symeda.sormas.api.importexport.ImportErrorException) PersonDto(de.symeda.sormas.api.person.PersonDto) ArrayList(java.util.ArrayList) EventParticipantDto(de.symeda.sormas.api.event.EventParticipantDto) VaccinationDto(de.symeda.sormas.api.vaccination.VaccinationDto) ValidationRuntimeException(de.symeda.sormas.api.utils.ValidationRuntimeException) Date(java.util.Date) InvalidColumnException(de.symeda.sormas.api.importexport.InvalidColumnException) ImportSimilarityResultOption(de.symeda.sormas.ui.importer.ImportSimilarityResultOption) EventParticipantCriteria(de.symeda.sormas.api.event.EventParticipantCriteria) ImportRelatedObjectsMapper(de.symeda.sormas.api.importexport.ImportRelatedObjectsMapper) PersonImportSimilarityResult(de.symeda.sormas.ui.importer.PersonImportSimilarityResult)

Example 5 with PersonImportSimilarityResult

use of de.symeda.sormas.ui.importer.PersonImportSimilarityResult in project SORMAS-Project by hzi-braunschweig.

the class EventParticipantImporterTest method testImportEventParticipantSimilaritySkip.

@Test
public void testImportEventParticipantSimilaritySkip() throws IOException, InvalidColumnException, InterruptedException, CsvValidationException, URISyntaxException {
    EventParticipantFacadeEjbLocal eventParticipantFacade = getBean(EventParticipantFacadeEjbLocal.class);
    RDCF rdcf = creator.createRDCF("Region", "District", "Community", "Facility");
    UserDto user = creator.createUser(rdcf.region.getUuid(), rdcf.district.getUuid(), rdcf.facility.getUuid(), "Surv", "Sup", UserRole.SURVEILLANCE_SUPERVISOR);
    EventDto event = creator.createEvent(EventStatus.SIGNAL, "Title", "Description", "First", "Name", "12345", TypeOfPlace.PUBLIC_PLACE, DateHelper.subtractDays(new Date(), 2), new Date(), user.toReference(), user.toReference(), Disease.EVD);
    EventReferenceDto eventRef = event.toReference();
    // Person Similarity: create
    File csvFile = new File(getClass().getClassLoader().getResource("sormas_eventparticipant_import_test_similarities.csv").toURI());
    EventParticipantImporterExtension eventParticipantImporter = new EventParticipantImporterExtension(csvFile, user, event) {

        @Override
        protected <T extends PersonImportSimilarityResult> void handlePersonSimilarity(PersonDto newPerson, Consumer<T> resultConsumer, BiFunction<SimilarPersonDto, ImportSimilarityResultOption, T> createSimilarityResult, String infoText, UI currentUI) {
            resultConsumer.accept((T) new PersonImportSimilarityResult(null, ImportSimilarityResultOption.SKIP));
        }
    };
    ImportResultStatus importResult = eventParticipantImporter.runImport();
    assertEquals(ImportResultStatus.COMPLETED, importResult);
    assertEquals(0, eventParticipantFacade.count(new EventParticipantCriteria().withEvent(eventRef)));
    assertEquals(0, getPersonFacade().getAllUuids().size());
}
Also used : PersonDto(de.symeda.sormas.api.person.PersonDto) SimilarPersonDto(de.symeda.sormas.api.person.SimilarPersonDto) UserDto(de.symeda.sormas.api.user.UserDto) EventDto(de.symeda.sormas.api.event.EventDto) Date(java.util.Date) RDCF(de.symeda.sormas.ui.TestDataCreator.RDCF) Consumer(java.util.function.Consumer) UI(com.vaadin.ui.UI) BiFunction(java.util.function.BiFunction) EventReferenceDto(de.symeda.sormas.api.event.EventReferenceDto) EventParticipantFacadeEjbLocal(de.symeda.sormas.backend.event.EventParticipantFacadeEjb.EventParticipantFacadeEjbLocal) ImportResultStatus(de.symeda.sormas.ui.importer.ImportResultStatus) EventParticipantCriteria(de.symeda.sormas.api.event.EventParticipantCriteria) File(java.io.File) PersonImportSimilarityResult(de.symeda.sormas.ui.importer.PersonImportSimilarityResult) AbstractBeanTest(de.symeda.sormas.ui.AbstractBeanTest) Test(org.junit.Test)

Aggregations

PersonImportSimilarityResult (de.symeda.sormas.ui.importer.PersonImportSimilarityResult)6 PersonDto (de.symeda.sormas.api.person.PersonDto)5 SimilarPersonDto (de.symeda.sormas.api.person.SimilarPersonDto)5 EventDto (de.symeda.sormas.api.event.EventDto)4 EventParticipantCriteria (de.symeda.sormas.api.event.EventParticipantCriteria)4 UserDto (de.symeda.sormas.api.user.UserDto)4 AbstractBeanTest (de.symeda.sormas.ui.AbstractBeanTest)4 ImportResultStatus (de.symeda.sormas.ui.importer.ImportResultStatus)4 File (java.io.File)4 ArrayList (java.util.ArrayList)4 Date (java.util.Date)4 Consumer (java.util.function.Consumer)4 Test (org.junit.Test)4 UI (com.vaadin.ui.UI)3 EventParticipantDto (de.symeda.sormas.api.event.EventParticipantDto)3 EventReferenceDto (de.symeda.sormas.api.event.EventReferenceDto)3 PersonSimilarityCriteria (de.symeda.sormas.api.person.PersonSimilarityCriteria)3 EventParticipantFacadeEjbLocal (de.symeda.sormas.backend.event.EventParticipantFacadeEjb.EventParticipantFacadeEjbLocal)3 RDCF (de.symeda.sormas.ui.TestDataCreator.RDCF)3 BiFunction (java.util.function.BiFunction)3