/** * Copyright [2015] [Christian Loehnert] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.ks.idnadrev.cost.csvimport; import com.google.common.base.Charsets; import de.ks.activity.ActivityController; import de.ks.datasource.DataSource; import de.ks.idnadrev.cost.pattern.view.BookingPatternParser; import de.ks.idnadrev.entity.cost.Account; import de.ks.idnadrev.entity.cost.Booking; import de.ks.persistence.PersistentWork; import de.ks.reflection.PropertyPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.persistence.criteria.Predicate; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; public class BookingFromCSVDS implements DataSource<ImporterBookingViewModel> { private static final Logger log = LoggerFactory.getLogger(BookingFromCSVDS.class); private static final String KEY_DESCRIPTION = PropertyPath.property(Booking.class, b -> b.getDescription()); private static final String KEY_BOOKINGTIME = PropertyPath.property(Booking.class, b -> b.getBookingTime()); private static final String KEY_AMOUNT = PropertyPath.property(Booking.class, b -> b.getAmount()); private File fileToLoad; @Inject ActivityController activityController; @Inject BookingPatternParser patternParser; @Override public ImporterBookingViewModel loadModel(Consumer<ImporterBookingViewModel> furtherProcessing) { ImporterBookingViewModel retval = new ImporterBookingViewModel(); CSVParseDefinitionController controller = activityController.getControllerInstance(CSVParseDefinitionController.class); String accountName = controller.account.getSelectionModel().getSelectedItem(); if (fileToLoad != null && accountName != null) { List<Booking> bookings = Collections.synchronizedList(new LinkedList<>()); LinkedList<CompletableFuture<Void>> futures = new LinkedList<>(); Account account = PersistentWork.forName(Account.class, accountName); BookingFromCSVImporter importer = controller.getImporter(); try { List<String> lines = Files.readAllLines(fileToLoad.toPath(), Charsets.ISO_8859_1); for (String line : lines) { try { Booking booking = importer.createBooking(line); booking.setAccount(account); CompletableFuture<Void> future = checkForExistingBooking(bookings, booking, retval); futures.add(future); } catch (Exception e) { log.debug("Error during parsing line \"{}\"", line, e); retval.addError(e, line); } } } catch (IOException e) { retval.addError(e); } futures.forEach(f -> f.join()); retval.getBookings().addAll(bookings); } return retval; } protected CompletableFuture<Void> checkForExistingBooking(List<Booking> bookings, Booking booking, ImporterBookingViewModel retval) { CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> booking, activityController.getExecutorService()).thenApply(b -> { List<Booking> found = PersistentWork.from(Booking.class, (root, query, builder) -> { Predicate sameDesc = builder.equal(root.get(KEY_DESCRIPTION), b.getDescription()); Predicate sameTime = builder.equal(root.get(KEY_BOOKINGTIME), b.getBookingTime()); Predicate sameAmount = builder.equal(root.get(KEY_AMOUNT), b.getAmount()); query.where(sameAmount, sameDesc, sameTime); }, null); if (found.isEmpty()) { if (b.getCategory() == null || b.getCategory().isEmpty()) { b.setCategory(patternParser.parseLine(b.getDescription())); } return b; } else { retval.addError("Booking " + booking.getDescription() + " already exists."); return null; } }).thenAccept(b -> { if (b != null) { bookings.add(b); } }); return future; } @Override public void saveModel(ImporterBookingViewModel model, Consumer<ImporterBookingViewModel> beforeSaving) { beforeSaving.accept(model); PersistentWork.run(em -> { List<Booking> bookingsToImport = model.getBookingsToImport(); for (Booking booking : bookingsToImport) { Account account = PersistentWork.reload(booking.getAccount()); booking.setAccount(account); em.persist(booking); } }); } @Override public void setLoadingHint(Object dataSourceHint) { if (dataSourceHint instanceof File) { this.fileToLoad = (File) dataSourceHint; } } }