package charts.builder.spreadsheet; import static charts.ChartType.LOADS_DIN; import static charts.ChartType.LOADS_PSII; import static charts.ChartType.LOADS_TSS; import static charts.ChartType.LOADS_PN; import static charts.ChartType.LOADS_PP; import java.awt.Color; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import charts.ChartType; import charts.Region; import charts.builder.DataSource.MissingDataException; import charts.jfree.Attribute; import charts.jfree.AttributeMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; public abstract class LoadsBuilder extends JFreeBuilder { protected static final String PERIOD = "period"; protected static final String TOTAL = "Total"; protected static enum Indicator { TSS("Total suspended solids", true), TP("Total phosphorus", false), PP("Particulate phosphorus", true), DIP(false), DOP(false), TN("Total nitrogen", false), PN("Particulate nitrogen", true), DIN("Dissolved Inorganic nitrogen", true), DON(false), PSII("Toxic pesticides", true); private String label; private boolean include; private Indicator(boolean include) { this.include = include; } private Indicator(String label, boolean include) { this(include); this.label = label; } public boolean include() { return include; } public String getLabel() { return label!=null?label:name(); } } protected static final ImmutableMap<Region, Integer> ROWS = new ImmutableMap.Builder<Region, Integer>() .put(Region.CAPE_YORK, 5) .put(Region.WET_TROPICS, 6) .put(Region.BURDEKIN, 7) .put(Region.MACKAY_WHITSUNDAY, 8) .put(Region.FITZROY, 9) .put(Region.BURNETT_MARY, 10) .put(Region.GBR, 12) .build(); protected static final ImmutableMap<ChartType, Indicator> INDICATORS = new ImmutableMap.Builder<ChartType, Indicator>() .put(LOADS_DIN, Indicator.DIN) .put(LOADS_PSII, Indicator.PSII) .put(LOADS_TSS, Indicator.TSS) .put(LOADS_PN, Indicator.PN) .put(LOADS_PP, Indicator.PP) .build(); private final SubstitutionKey LAST_PERIOD = new SubstitutionKey("lastPeriod", "last period of loads data in the spreadsheet", new SubstitutionKey.Val() { @Override public String value(Context ctx) { return lastPeriod(ctx); } }); private final SubstitutionKey LAST_PERIOD_YYYY = new SubstitutionKey("lastPeriodyyyy", "last period of loads data in the spreadsheet format with 4 digit year", new SubstitutionKey.Val() { @Override public String value(Context ctx) { return formatPeriod(lastPeriod(ctx)); } }); public LoadsBuilder(ChartType type) { super(type); } public LoadsBuilder(List<ChartType> types) { super(types); } @Override public boolean canHandle(SpreadsheetDataSource ds) { try { return StringUtils.containsIgnoreCase(ds.select("C1").asString(), "Summary of Load Reductions") && indicatorsOk(ds); } catch(MissingDataException e) { return false; } } private boolean indicatorsOk(SpreadsheetDataSource ds) throws MissingDataException { Set<Indicator> iset = Sets.newHashSet(); for(Indicator i : INDICATORS.values()) { if(i.include()) { iset.add(i); } } for(int col = 0; col< ds.getColumnCount(1);col++) { String s = StringUtils.strip(ds.select(1, col).asString()); if(StringUtils.isNotBlank(s)) { Indicator i = Indicator.valueOf(StringUtils.upperCase(s)); if(i != null) { iset.remove(i); } } } return iset.isEmpty(); } @Override protected Map<String, List<String>> getParameters(SpreadsheetDataSource datasource, ChartType type) { // only the 'Total' period can be selected currently. return new ImmutableMap.Builder<String, List<String>>() .put(PERIOD, Lists.newArrayList(TOTAL)) .build(); } private List<String> getPeriods(SpreadsheetDataSource ds, Indicator indicator) { try { List<String> periods = Lists.newArrayList(); for(int col = findIndicatorStartColumn(ds, indicator); col<=findIndicatorEndColumn(ds, indicator);col++) { periods.add(ds.select(2, col).asString()); } return periods; } catch(MissingDataException e) { throw new RuntimeException(e); } } protected Double selectAsDouble(SpreadsheetDataSource ds, Region region, Indicator indicator, String period) throws MissingDataException { List<String> periods = getPeriods(ds, indicator); int row = ROWS.get(region) - 1; int col = findIndicatorStartColumn(ds, indicator) + periods.indexOf(period); Double val = ds.select(row, col).asDouble(); return val; } private int findIndicatorStartColumn(SpreadsheetDataSource ds, Indicator indicator) throws MissingDataException { for(int i=0;i<ds.getColumns(1);i++) { if(StringUtils.equalsIgnoreCase(indicator.name(), ds.select(1, i).asString())) { return findIndicatorStartColumn(ds, i, indicator); } } throw new RuntimeException("can't find column start for indicator "+indicator); } private int findIndicatorStartColumn(SpreadsheetDataSource ds, int col, Indicator indicator) throws MissingDataException { for(int i=col;i>=0;i--) { String s = StringUtils.strip(ds.select(2, i-1).asString()); if(StringUtils.equalsIgnoreCase("total", s) || (i <= 3)) { return i; } } throw new RuntimeException("can't find column start for indicator (0) "+indicator); } private int findIndicatorEndColumn(SpreadsheetDataSource ds, Indicator indicator) throws MissingDataException { int start = findIndicatorStartColumn(ds, indicator); for(int i = start;i<ds.getColumnCount(2);i++) { String s = StringUtils.strip(ds.select(2, i).asString()); if(StringUtils.equalsIgnoreCase("total", s)) { return i; } } throw new RuntimeException("can't find column end for indicator "+indicator); } @Override public AttributeMap defaults(ChartType type) { return new AttributeMap.Builder(). put(Attribute.Y_AXIS_LABEL, "% Load reduction"). put(Attribute.SERIES_COLOR, Color.blue). build(); } @Override public Set<SubstitutionKey> substitutionKeys() { return ImmutableSet.<SubstitutionKey>builder(). addAll(super.substitutionKeys()).add(LAST_PERIOD).add(LAST_PERIOD_YYYY).build(); } private String lastPeriod(Context ctx) { Indicator indicator = getIndicator(ctx.type()); List<String> periods = getPeriods(ctx.datasource(), indicator); if(periods.size() >= 2) { return periods.get(periods.size()-2); } else { return ""; } } private String formatPeriod(String period) { try { String p = StringUtils.substringBetween(period, "(", ")"); String[] sa = StringUtils.split(p, "/"); if(sa[0].length() == 2 && sa[1].length() == 2) { return "20"+sa[0]+"-"+"20"+sa[1]; } return period; } catch(Exception e) { return period; } } protected Indicator getIndicator(ChartType type) { Indicator indicator = INDICATORS.get(type); return indicator != null ? indicator : Indicator.TSS; } }