package de.onyxbits.tradetrax.pages.tools; import java.io.StringReader; import java.sql.Timestamp; import java.text.DateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Vector; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.tapestry5.alerts.AlertManager; import org.apache.tapestry5.alerts.Duration; import org.apache.tapestry5.alerts.Severity; import org.apache.tapestry5.annotations.Component; import org.apache.tapestry5.annotations.Persist; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.beaneditor.BeanModel; import org.apache.tapestry5.beaneditor.PropertyModel; import org.apache.tapestry5.corelib.components.Form; import org.apache.tapestry5.corelib.components.TextArea; import org.apache.tapestry5.hibernate.annotations.CommitAfter; import org.apache.tapestry5.ioc.Messages; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.services.BeanModelSource; import org.hibernate.Session; import de.onyxbits.tradetrax.entities.IdentUtil; import de.onyxbits.tradetrax.entities.Name; import de.onyxbits.tradetrax.entities.Stock; import de.onyxbits.tradetrax.entities.Variant; import de.onyxbits.tradetrax.pages.Index; import de.onyxbits.tradetrax.services.EventLogger; import de.onyxbits.tradetrax.services.MoneyRepresentation; import de.onyxbits.tradetrax.services.SettingsStore; /** * Simple tool for batch importing CSV assets. * * @author patrick * */ public class Importer { @Inject private AlertManager alertManager; @Inject private SettingsStore settingsStore; @Inject private BeanModelSource ledgerSource; @Inject private Messages messages; @Inject private Session session; @Component(id = "csvForm") private Form csvForm; @Component(id = "rawcsvField") private TextArea rawcsvField; @Component(id = "commitForm") private Form commitForm; @Property @Persist private String rawcsv; @Property private Stock row; @Inject private MoneyRepresentation moneyRepresentation; private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM); @Persist @Property private List<Stock> parsed; @Inject private EventLogger eventLogger; public BeanModel<Stock> getLedgerModel() { BeanModel<Stock> model = ledgerSource.createDisplayModel(Stock.class, messages); List<String> lst = model.getPropertyNames(); for (String s : lst) { PropertyModel nameColumn = model.getById(s); nameColumn.sortable(false); } return model; } public void setupRender() { if (rawcsv == null || rawcsv.length() == 0) { rawcsv = "name,variant,location,acquired,cost,units,liquidated,returns"; } } public Importer onSuccessFromCsvForm() { CSVParser parser = null; parsed = new Vector<Stock>(); try { parser = new CSVParser(new StringReader(rawcsv), CSVFormat.EXCEL.withHeader()); for (CSVRecord rec : parser) { Stock stock = recordToStock(rec); if (stock != null) { parsed.add(stock); } } } catch (Exception e) { } try { parser.close(); } catch (Exception e) { } return this; } @CommitAfter public Object onSuccessFromCommitForm() { if (parsed == null) { return this; } int count = 0; for (Stock stock : parsed) { // We have to repack name/variant here because it is highly likely that // the CSV contains a label several times which is not yet known to the // database. So we need a save() after every findName() to make sure we // don't violate the unique constraint. stock.setName(IdentUtil.findName(session, stock.getName().getLabel())); Variant v = stock.getVariant(); if (v != null) { stock.setVariant(IdentUtil.findVariant(session, stock.getVariant().getLabel())); } session.save(stock); eventLogger.acquired(stock); count++; } alertManager.alert(Duration.SINGLE, Severity.SUCCESS, messages.format("success", count)); parsed = null; return Index.class; } private Stock recordToStock(CSVRecord rec) { try { Stock stock = new Stock(); // A name is a must! Name name = new Name(); name.setLabel(cleanName(rec.get("name"))); stock.setName(name); if (rec.isMapped("variant") && rec.get("variant").length() > 0) { Variant variant = new Variant(); String tmp = rec.get("variant"); if (tmp != null) { variant.setLabel(tmp); stock.setVariant(variant); } } if (rec.isMapped("acquired")) { stock.setAcquired(parseDate(rec.get("acquired"))); } if (rec.isMapped("cost")) { stock.setBuyPrice(parsePrice(rec.get("cost"))); } if (rec.isMapped("units")) { stock.setUnitCount(parseAmount(rec.get("units"))); } if (rec.isMapped("liquidated")) { stock.setLiquidated(parseDate(rec.get("liquidated"))); } if (rec.isMapped("returns")) { stock.setSellPrice(parsePrice(rec.get("returns"))); } if (rec.isMapped("location")) { stock.setLocation(rec.get("location")); } return stock; } catch (Exception e) { } return null; } private String cleanName(String name) { if (name == null) { return null; } // The database is ok with any string, the UI is not, so prevent the user // from entering what cannot be edited. String ret = name.trim(); if (ret.length() == 0) { return messages.format("noname"); } return ret.replace('\n', ' ').replace('\t', ' '); } private int parseAmount(String string) { try { return Integer.valueOf(string); } catch (Exception e) { } return 1; } private long parsePrice(String string) { try { return moneyRepresentation.userToDatabase(string, 1); } catch (Exception e) { e.printStackTrace(); } return 0; } private Date parseDate(String string) { if (string != null && string.length() == 0) { Calendar now = Calendar.getInstance(); now.set(Calendar.MILLISECOND, 0); now.set(Calendar.SECOND, 0); now.set(Calendar.MINUTE, 0); now.set(Calendar.HOUR_OF_DAY, 0); return now.getTime(); } try { return Timestamp.valueOf(string); } catch (Exception e) { } try { return dateFormat.parse(string); } catch (Exception e) { } return null; } }