/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.services.stats; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.List; import nl.strohalm.cyclos.dao.ads.AdDAO; import nl.strohalm.cyclos.entities.accounts.Currency; import nl.strohalm.cyclos.entities.accounts.transactions.PaymentFilter; import nl.strohalm.cyclos.entities.ads.Ad; import nl.strohalm.cyclos.entities.groups.Group; import nl.strohalm.cyclos.entities.members.MemberQuery; import nl.strohalm.cyclos.entities.reports.StatisticalDTO; import nl.strohalm.cyclos.entities.reports.StatisticalKeyDevelopmentsQuery; import nl.strohalm.cyclos.entities.reports.StatisticalNumber; import nl.strohalm.cyclos.entities.reports.StatisticalQuery; import nl.strohalm.cyclos.entities.reports.ThroughTimeRange; import nl.strohalm.cyclos.services.stats.StatisticalResultDTO.MultiGraph; import nl.strohalm.cyclos.services.stats.general.FilterUsed; import nl.strohalm.cyclos.services.stats.general.KeyDevelopmentsStatsPerMonthVO; import nl.strohalm.cyclos.utils.Period; import nl.strohalm.cyclos.utils.query.PageHelper; import nl.strohalm.cyclos.utils.statistics.ListOperations; import nl.strohalm.cyclos.utils.statistics.Median; /** * An implementation of the StatisticalKeyDevelopmentsService interface; subclasses StatisticalService. As Statistical Key Developments are not * statistics (in the sense that numbers are not retrieved by taking the mean or median from a set of observations), but are just totals, there is no * need to apply any limit on the minimum number of values. * @author rinke * */ public class StatisticalKeyDevelopmentsServiceImpl extends StatisticalServiceImpl implements StatisticalKeyDevelopmentsServiceLocal { private AdDAO adDao; private StatisticalKeyDevelopmentsQuery savedParameters; private List<Number> transactionAmounts; private List<Number> transactionAmounts2; public StatisticalResultDTO getComparePeriodsGrossProduct(final StatisticalKeyDevelopmentsQuery queryParameters) { final byte precision = (byte) getLocalSettings().getPrecision().getValue(); final Period periodMain = queryParameters.getPeriodMain(); final Period periodComparedTo = queryParameters.getPeriodComparedTo(); final Collection<? extends Group> groups = queryParameters.getGroups(); final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); // Data structure to build the table final Number[][] tableCells = new Number[1][3]; // Sum of amounts of transfers (main period with payment filter) final BigDecimal periodMainSumFilter = getSumOfTransactions(periodMain, groups, paymentFilter); // Sum of amounts of transfers (compared to period with payment filter) final BigDecimal periodComparedToSumFilter = getSumOfTransactions(periodComparedTo, groups, paymentFilter); // Row for payment filter tableCells[0][1] = new StatisticalNumber(periodMainSumFilter.doubleValue(), precision); tableCells[0][0] = new StatisticalNumber(periodComparedToSumFilter.doubleValue(), precision); tableCells[0][2] = StatisticalNumber.createPercentage(periodMainSumFilter, periodComparedToSumFilter); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.grossProduct"); String[] rowKeys; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } passCurrencyCompared(result, queryParameters); try { result.setRowKeys(rowKeys); applyColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in Gross Product"); e.printStackTrace(); } if (queryParameters.isGrossProductGraph()) { result.setGraphDimensions(null, 2, null); result.setGraphType(StatisticalResultDTO.GraphType.BAR); } return result; } public StatisticalResultDTO getComparePeriodsHighestTransactionAmount(final StatisticalKeyDevelopmentsQuery queryParameters) { final byte precision = (byte) getLocalSettings().getPrecision().getValue(); final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); // Data structure to build the table final String baseKey = "reports.stats.keydevelopments.highestAmountPerTransaction"; final Number[][] tableCells = new Number[1][3]; final List<Number> periodMainAmounts = getTransactionAmounts(queryParameters, paymentFilter); final List<Number> periodComparedAmounts = getTransactionAmountsComparedTo(queryParameters, paymentFilter); if (periodMainAmounts.size() < StatisticalService.MINIMUM_NUMBER_OF_VALUES && periodComparedAmounts.size() < StatisticalService.MINIMUM_NUMBER_OF_VALUES) { return StatisticalResultDTO.noDataAvailable(baseKey); } final Number periodMainHighestAmount = ListOperations.getMax(periodMainAmounts); final Number periodComparedToHighestAmount = ListOperations.getMax(periodComparedAmounts); tableCells[0][1] = (periodMainHighestAmount == null) ? new StatisticalNumber() : new StatisticalNumber(periodMainHighestAmount.doubleValue(), precision); tableCells[0][0] = (periodComparedToHighestAmount == null) ? new StatisticalNumber() : new StatisticalNumber(periodComparedToHighestAmount.doubleValue(), precision); tableCells[0][2] = StatisticalNumber.createPercentage(periodMainHighestAmount, periodComparedToHighestAmount); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey(baseKey); String[] rowKeys; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } passCurrencyCompared(result, queryParameters); try { result.setRowKeys(rowKeys); if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); } applyColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in highest transaction amount per transaction"); e.printStackTrace(); } if (queryParameters.isTransactionAmountGraph()) { result.setGraphDimensions(null, 2, null); result.setGraphType(StatisticalResultDTO.GraphType.BAR); } return result; } public StatisticalResultDTO getComparePeriodsMedianAmountPerTransaction(final StatisticalKeyDevelopmentsQuery queryParameters) { final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); // Data structure to build the table final Number[][] tableCells = new Number[1][4]; final String baseKey = "reports.stats.keydevelopments.averageAmountPerTransaction"; final List<Number> periodMainAmounts = getTransactionAmounts(queryParameters, paymentFilter); final List<Number> periodComparedAmounts = getTransactionAmountsComparedTo(queryParameters, paymentFilter); if (periodMainAmounts.size() < StatisticalService.MINIMUM_NUMBER_OF_VALUES && periodComparedAmounts.size() < StatisticalService.MINIMUM_NUMBER_OF_VALUES) { return StatisticalResultDTO.noDataAvailable(baseKey); } // Row for payment filter tableCells[0][1] = Median.getMedian(periodMainAmounts, StatisticalService.ALPHA); tableCells[0][0] = Median.getMedian(periodComparedAmounts, StatisticalService.ALPHA); tableCells[0][2] = StatisticalNumber.createPercentage(tableCells[0][1], tableCells[0][0]); tableCells[0][3] = StatisticalServiceImpl.calculatePvalue(periodMainAmounts, periodComparedAmounts); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey(baseKey); String[] rowKeys = null; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } final Currency currency = getCurrency(queryParameters); result.setColumnSubHeaders(new String[] { parenthesizeString(currency.getSymbol()), parenthesizeString(currency.getSymbol()), "", "" }); result.setYAxisUnits(currency.getSymbol()); try { result.setRowKeys(rowKeys); final String[] columnKeys = { "", "", "reports.stats.general.growth", "reports.stats.general.p" }; result.setColumnKeys(columnKeys); result.setColumnHeader(queryParameters.getPeriodMain().getName(), 1); result.setColumnHeader(queryParameters.getPeriodComparedTo().getName(), 0); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in average amount per transaction"); e.printStackTrace(); } if (queryParameters.isTransactionAmountGraph()) { result.setGraphDimensions(null, 2, null); result.setGraphType(StatisticalResultDTO.GraphType.BAR); } return result; } public StatisticalResultDTO getComparePeriodsNumberOfAds(final StatisticalKeyDevelopmentsQuery queryParameters) { final Period periodMain = queryParameters.getPeriodMain(); final Period periodComparedTo = queryParameters.getPeriodComparedTo(); final Collection<? extends Group> groups = queryParameters.getGroups(); // Period "main" data final Integer activeAdsPeriodMain = adDao.getNumberOfAds(periodMain.getEnd(), groups, Ad.Status.ACTIVE); final Integer scheduledAdsPeriodMain = adDao.getNumberOfAds(periodMain.getEnd(), groups, Ad.Status.SCHEDULED); final Integer expiredAdsPeriodMain = adDao.getNumberOfAds(periodMain.getEnd(), groups, Ad.Status.EXPIRED); final Integer createdAdsPeriodMain = adDao.getNumberOfCreatedAds(periodMain, groups); // Period "compared to" data final Integer activeAdsPeriodComparedTo = adDao.getNumberOfAds(periodComparedTo.getEnd(), groups, Ad.Status.ACTIVE); final Integer scheduledAdsPeriodComparedTo = adDao.getNumberOfAds(periodComparedTo.getEnd(), groups, Ad.Status.SCHEDULED); final Integer expiredAdsPeriodComparedTo = adDao.getNumberOfAds(periodComparedTo.getEnd(), groups, Ad.Status.EXPIRED); final Integer createdAdsPeriodComparedTo = adDao.getNumberOfCreatedAds(periodComparedTo, groups); // Data structure to build the table final Number[][] tableCells = new Number[4][3]; // First row (active ads at the end of the period) tableCells[0][1] = new StatisticalNumber(activeAdsPeriodMain); tableCells[0][0] = new StatisticalNumber(activeAdsPeriodComparedTo); tableCells[0][2] = StatisticalNumber.createPercentage(activeAdsPeriodMain, activeAdsPeriodComparedTo); // Second row (scheduled ads at the end of the period) tableCells[1][1] = new StatisticalNumber(scheduledAdsPeriodMain); tableCells[1][0] = new StatisticalNumber(scheduledAdsPeriodComparedTo); tableCells[1][2] = StatisticalNumber.createPercentage(scheduledAdsPeriodMain, scheduledAdsPeriodComparedTo); // Third row (expired ads at the end of the period) tableCells[2][1] = new StatisticalNumber(expiredAdsPeriodMain); tableCells[2][0] = new StatisticalNumber(expiredAdsPeriodComparedTo); tableCells[2][2] = StatisticalNumber.createPercentage(expiredAdsPeriodMain, expiredAdsPeriodComparedTo); // Forth row (created ads in period) tableCells[3][1] = new StatisticalNumber(createdAdsPeriodMain); tableCells[3][0] = new StatisticalNumber(createdAdsPeriodComparedTo); tableCells[3][2] = StatisticalNumber.createPercentage(createdAdsPeriodMain, createdAdsPeriodComparedTo); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.numberOfAds"); final String[] rowKeys = { "reports.stats.keydevelopments.numberOfAds.active", "reports.stats.keydevelopments.numberOfAds.scheduled", "reports.stats.keydevelopments.numberOfAds.expired", "reports.stats.keydevelopments.numberOfAds.created" }; result.setFilterAsNotUsed(FilterUsed.FilterType.PAYMENT); try { result.setRowKeys(rowKeys); applyColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in number of ads"); e.printStackTrace(); } if (queryParameters.isNumberOfAdsGraph()) { result.setGraphDimensions(null, 2, null); result.setGraphType(StatisticalResultDTO.GraphType.BAR); } return result; } public StatisticalResultDTO getComparePeriodsNumberOfMembers(final StatisticalKeyDevelopmentsQuery queryParameters) { final Period periodMain = queryParameters.getPeriodMain(); final Period periodComparedTo = queryParameters.getPeriodComparedTo(); final Collection<? extends Group> groups = queryParameters.getGroups(); // Number of members / main period final int periodMainMembersCount = getElementDao().getNumberOfMembersInGroupsInPeriod(groups, periodMain); final int periodComparedToMembersCount = getElementDao().getNumberOfMembersInGroupsInPeriod(groups, periodComparedTo); // Number of new members / main period final int periodMainNewMembersCount = getNewMembersCount(periodMain, groups); // Number of new members / compared to period final int periodComparedToNewMembersCount = getNewMembersCount(periodComparedTo, groups); // Number of disappeared members / main period final int periodMainDisappearedMembersCount = getDisappearedMembersCount(periodMain, groups); // Number of disappeared members / compared to period final int periodComparedToDisappearedMembersCount = getDisappearedMembersCount(periodComparedTo, groups); // Data structure to build the table final Number[][] tableCells = new Number[3][3]; // First row tableCells[0][1] = new StatisticalNumber(periodMainMembersCount); tableCells[0][0] = new StatisticalNumber(periodComparedToMembersCount); tableCells[0][2] = StatisticalNumber.createPercentage(periodMainMembersCount, periodComparedToMembersCount); // Second row tableCells[1][1] = new StatisticalNumber(periodMainNewMembersCount); tableCells[1][0] = new StatisticalNumber(periodComparedToNewMembersCount); tableCells[1][2] = StatisticalNumber.createPercentage(periodMainNewMembersCount, periodComparedToNewMembersCount); // Third row tableCells[2][1] = new StatisticalNumber(periodMainDisappearedMembersCount); tableCells[2][0] = new StatisticalNumber(periodComparedToDisappearedMembersCount); tableCells[2][2] = StatisticalNumber.createPercentage(periodMainDisappearedMembersCount, periodComparedToDisappearedMembersCount); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.numberOfMembers"); final String[] rowKeys = { "reports.stats.keydevelopments.numberOfMembers.numberOfMembers", "reports.stats.keydevelopments.numberOfMembers.numberOfNewMembers", "reports.stats.keydevelopments.numberOfMembers.numberOfDisappearedMembers" }; try { result.setRowKeys(rowKeys); applyColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in NumberOfMembers!"); e.printStackTrace(); } if (queryParameters.isNumberOfMembersGraph()) { result.setGraphDimensions(null, 2, null); result.setGraphType(StatisticalResultDTO.GraphType.BAR); } return result; } public StatisticalResultDTO getComparePeriodsNumberOfTransactions(final StatisticalKeyDevelopmentsQuery queryParameters) { final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); // Data structure to build the table final Number[][] tableCells = new Number[1][3]; final Integer periodMainCount = getTransactionAmounts(queryParameters, paymentFilter).size(); final Integer periodComparedToCount = getTransactionAmountsComparedTo(queryParameters, paymentFilter).size(); tableCells[0][1] = new StatisticalNumber(periodMainCount); tableCells[0][0] = new StatisticalNumber(periodComparedToCount); tableCells[0][2] = StatisticalNumber.createPercentage(periodMainCount, periodComparedToCount); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.numberOfTransactions"); String[] rowKeys; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } try { result.setRowKeys(rowKeys); if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); } applyColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in NumberOfTransactions"); e.printStackTrace(); } if (queryParameters.isNumberOfTransactionsGraph()) { result.setGraphDimensions(null, 2, null); result.setGraphType(StatisticalResultDTO.GraphType.BAR); } return result; } public StatisticalResultDTO getSinglePeriodGrossProduct(final StatisticalKeyDevelopmentsQuery queryParameters) { final byte precision = (byte) getLocalSettings().getPrecision().getValue(); final Period periodMain = queryParameters.getPeriodMain(); final Collection<? extends Group> groups = queryParameters.getGroups(); final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); // Data structure to build the table final Number[][] tableCells = new Number[1][1]; tableCells[0][0] = new StatisticalNumber(getSumOfTransactions(periodMain, groups, paymentFilter).doubleValue(), precision); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.grossProduct"); String[] rowKeys; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } passCurrencySingle(result, queryParameters); try { result.setRowKeys(rowKeys); applySinglePeriodColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in Gross Product"); e.printStackTrace(); } return result; } public StatisticalResultDTO getSinglePeriodHighestTransactionAmount(final StatisticalKeyDevelopmentsQuery queryParameters) { final byte precision = (byte) getLocalSettings().getPrecision().getValue(); final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); // Data structure to build the table final String baseKey = "reports.stats.keydevelopments.highestAmountPerTransaction"; final Number[][] tableCells = new Number[1][1]; final List<Number> amounts = getTransactionAmounts(queryParameters, paymentFilter); if (amounts.size() < StatisticalService.MINIMUM_NUMBER_OF_VALUES) { return StatisticalResultDTO.noDataAvailable(baseKey); } // getting a list and iterating over it is much faster than using MySql's max function in a query. tableCells[0][0] = new StatisticalNumber(ListOperations.getMax(amounts).doubleValue(), precision); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey(baseKey); String[] rowKeys; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } passCurrencySingle(result, queryParameters); try { result.setRowKeys(rowKeys); if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); } applySinglePeriodColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in highest transaction amount per transaction"); e.printStackTrace(); } return result; } public StatisticalResultDTO getSinglePeriodMedianAmountPerTransaction(final StatisticalKeyDevelopmentsQuery queryParameters) { // a separate query is needed, as gross product is only on incoming, and number of trans is on all transactions final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); final String baseKey = "reports.stats.keydevelopments.averageAmountPerTransaction"; final StatisticalNumber amount = Median.getMedian(getTransactionAmounts(queryParameters, paymentFilter), StatisticalService.ALPHA); if (amount.isNull()) { return StatisticalResultDTO.noDataAvailable(baseKey); } final Number[][] tableCells = new Number[1][1]; tableCells[0][0] = amount; final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey(baseKey); String[] rowKeys; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } passCurrencySingle(result, queryParameters); try { result.setRowKeys(rowKeys); applySinglePeriodColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in average amount per transaction"); e.printStackTrace(); } return result; } public StatisticalResultDTO getSinglePeriodNumberOfAds(final StatisticalKeyDevelopmentsQuery queryParameters) { final Period periodMain = queryParameters.getPeriodMain(); final Collection<? extends Group> groups = queryParameters.getGroups(); // Period main data final Integer activeAdsPeriodMain = adDao.getNumberOfAds(periodMain.getEnd(), groups, Ad.Status.ACTIVE); final Integer scheduledAdsPeriodMain = adDao.getNumberOfAds(periodMain.getEnd(), groups, Ad.Status.SCHEDULED); final Integer expiredAdsPeriodMain = adDao.getNumberOfAds(periodMain.getEnd(), groups, Ad.Status.EXPIRED); final Integer createdAdsPeriodMain = adDao.getNumberOfCreatedAds(periodMain, groups); // Data structure to build the table final Number[][] tableCells = new Number[4][1]; // First row (active ads at the end of the period) tableCells[0][0] = new StatisticalNumber(activeAdsPeriodMain); // Second row (scheduled ads at the end of the period) tableCells[1][0] = new StatisticalNumber(scheduledAdsPeriodMain); // Third row (expired ads at the end of the period) tableCells[2][0] = new StatisticalNumber(expiredAdsPeriodMain); // Forth row (created ads in period) tableCells[3][0] = new StatisticalNumber(createdAdsPeriodMain); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.numberOfAds"); result.setFilterAsNotUsed(FilterUsed.FilterType.PAYMENT); final String[] rowKeys = { "reports.stats.keydevelopments.numberOfAds.active", "reports.stats.keydevelopments.numberOfAds.scheduled", "reports.stats.keydevelopments.numberOfAds.expired", "reports.stats.keydevelopments.numberOfAds.created" }; try { result.setRowKeys(rowKeys); applySinglePeriodColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in number of ads"); e.printStackTrace(); } return result; } public StatisticalResultDTO getSinglePeriodNumberOfMembers(final StatisticalKeyDevelopmentsQuery queryParameters) { final Period periodMain = queryParameters.getPeriodMain(); final Collection<? extends Group> groups = queryParameters.getGroups(); // Number of members final int periodMainMembersCount = getElementDao().getNumberOfMembersInGroupsInPeriod(groups, periodMain); // Number of new members final int periodMainNewMembersCount = getNewMembersCount(periodMain, groups); // Number of disappeared members final int periodMainDisappearedMembersCount = getDisappearedMembersCount(periodMain, groups); // Data structure to build the table final Number[][] tableCells = new Number[3][1]; // First row tableCells[0][0] = new StatisticalNumber(periodMainMembersCount); // Second row tableCells[1][0] = new StatisticalNumber(periodMainNewMembersCount); // Third row tableCells[2][0] = new StatisticalNumber(periodMainDisappearedMembersCount); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.numberOfMembers"); final String[] rowKeys = { "reports.stats.keydevelopments.numberOfMembers.numberOfMembers", "reports.stats.keydevelopments.numberOfMembers.numberOfNewMembers", "reports.stats.keydevelopments.numberOfMembers.numberOfDisappearedMembers" }; try { result.setRowKeys(rowKeys); applySinglePeriodColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in NumberOfMembers!"); e.printStackTrace(); } return result; } public StatisticalResultDTO getSinglePeriodNumberOfTransactions(final StatisticalKeyDevelopmentsQuery queryParameters) { final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); // Data structure to build the table final Number[][] tableCells = new Number[1][1]; tableCells[0][0] = new StatisticalNumber(getTransactionAmounts(queryParameters, paymentFilter).size()); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); result.setBaseKey("reports.stats.keydevelopments.numberOfTransactions"); String[] rowKeys; if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); rowKeys = new String[] { "" }; } else { rowKeys = new String[] { "reports.stats.keydevelopments.grossProduct.allTransactions" }; } try { result.setRowKeys(rowKeys); if (paymentFilter != null) { result.setRowHeader(paymentFilter.getName(), 0); } applySinglePeriodColumnHeadersAndKeys(result, queryParameters); passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); } catch (final Exception e) { System.out.println("Error in NumberOfTransactions"); e.printStackTrace(); } return result; } public StatisticalResultDTO getThroughTheTime(final StatisticalKeyDevelopmentsQuery queryParameters) { // Query parameters final boolean isNumberOfMembers = queryParameters.isNumberOfMembers(); final boolean isGrossProduct = queryParameters.isGrossProduct(); final boolean isNumberOfTransactions = queryParameters.isNumberOfTransactions(); final boolean isTransactionAmount = queryParameters.isTransactionAmount(); final boolean isAds = queryParameters.isNumberOfAds(); final ThroughTimeRange throughTimeRange = queryParameters.getThroughTimeRange(); // get the units final Currency currency = getCurrency(queryParameters); // Prepare columns metadata final ArrayList<String> columnKeysList = new ArrayList<String>(5); final ArrayList<String> columnSubHeaderList = new ArrayList<String>(5); int columns = 0; if (isNumberOfMembers) { columnKeysList.add("reports.stats.keydevelopments.numberOfMembers.numberOfMembers"); columnSubHeaderList.add(""); columns++; } if (isGrossProduct) { columnKeysList.add("reports.stats.keydevelopments.grossProduct"); columnSubHeaderList.add(parenthesizeString(currency.getSymbol())); columns++; } if (isNumberOfTransactions) { columnKeysList.add("reports.stats.keydevelopments.numberOfTransactions"); columnSubHeaderList.add(""); columns++; } if (isTransactionAmount) { columnKeysList.add("reports.stats.keydevelopments.transactionAmount.median"); columnSubHeaderList.add(parenthesizeString(currency.getSymbol())); columns++; } if (isAds) { columnKeysList.add("reports.stats.keydevelopments.numberOfAds.active"); columnSubHeaderList.add(""); columns++; } final String[] columnKeys = columnKeysList.toArray(new String[0]); final String[] columnSubHeaders = columnSubHeaderList.toArray(new String[0]); // Prepare table dates final Period[] periods = queryParameters.getPeriods(); // Prepare data for table and chart final String[] rowHeaders = new String[periods.length]; final Number[][] tableCells = getThroughTheTimeCalculation(periods, queryParameters, columns, rowHeaders); final StatisticalResultDTO result = new StatisticalResultDTO(tableCells); String baseKey = "reports.stats.keydevelopments.throughTime"; passGroupFilter(result, queryParameters); passPaymentFilter(result, queryParameters); if (throughTimeRange == ThroughTimeRange.YEAR) { baseKey += ".years"; } else if (throughTimeRange == ThroughTimeRange.QUARTER) { baseKey += ".quarters"; } else { baseKey += ".months"; } result.setBaseKey(baseKey); result.setRowHeaders(rowHeaders); result.setColumnKeys(columnKeys); result.setColumnSubHeaders(columnSubHeaders); result.setMultiGraph(MultiGraph.BY_COLUMN); if (queryParameters.isThruTimeGraph()) { result.setGraphType(StatisticalResultDTO.GraphType.LINE); } return result; } public void setAdDao(final AdDAO adDao) { this.adDao = adDao; } /** * Creates the column headers and keys from the query. This is (usually) namePeriod1, namePeriod2 and "Growth". Beware that it also sets the * columnSubHeaders. If you reset this, the third column subHeader will be overwritten. * @param result the raw data object * @param queryParameters */ private void applyColumnHeadersAndKeys(final StatisticalResultDTO result, final StatisticalQuery queryParameters) { final String[] columnKeys = { "", "", "reports.stats.general.growth" }; result.setColumnKeys(columnKeys); result.setColumnHeader(queryParameters.getPeriodMain().getName(), 1); result.setColumnHeader(queryParameters.getPeriodComparedTo().getName(), 0); } /** * Creates the column headers and keys from the query. This is namePeriod1. Beware that it also sets the columnSubHeaders. If you reset this, the * third column subHeader will be overwritten. * @param result the raw data object * @param queryParameters */ private void applySinglePeriodColumnHeadersAndKeys(final StatisticalResultDTO result, final StatisticalQuery queryParameters) { final String[] columnKeys = { "" }; result.setColumnKeys(columnKeys); result.setColumnHeader(queryParameters.getPeriodMain().getName(), 0); } private int getDisappearedMembersCount(final Period period, final Collection<? extends Group> groups) { final MemberQuery memberQuery = new MemberQuery(); memberQuery.setDeactivationPeriod(period); memberQuery.setGroups(groups); memberQuery.setPageForCount(); return PageHelper.getTotalCount(getElementDao().searchHistoryRemoved(memberQuery)); } private int getNewMembersCount(final Period period, final Collection<? extends Group> groups) { final MemberQuery memberQuery = new MemberQuery(); memberQuery.setCreationPeriod(period); memberQuery.setGroups(groups); memberQuery.setPageForCount(); return PageHelper.getTotalCount(getElementDao().searchHistoryNew(memberQuery)); } private BigDecimal getSumOfTransactions(final Period period, final Collection<? extends Group> groups, final PaymentFilter paymentFilter) { final StatisticalDTO dto = new StatisticalDTO(); dto.setPeriod(period); dto.setGroups(groups); dto.setPaymentFilter(paymentFilter); return getTransferDao().getSumOfTransactions(dto); } private Number[][] getThroughTheTimeCalculation(final Period[] periods, final StatisticalKeyDevelopmentsQuery queryParameters, final int columns, final String[] rowHeaders) { final byte precision = (byte) getLocalSettings().getPrecision().getValue(); final Collection<? extends Group> groups = queryParameters.getGroups(); final PaymentFilter paymentFilter = getInitializedPaymentFilter(queryParameters); final Number[][] tableCells = new Number[periods.length][columns]; final Period totalPeriod = new Period(periods[0].getBegin(), periods[periods.length - 1].getEnd()); final ThroughTimeRange throughTimeRange = queryParameters.getThroughTimeRange(); final boolean isNumberOfMembers = queryParameters.isNumberOfMembers(); final boolean isGrossProduct = queryParameters.isGrossProduct(); final boolean isNumberOfTransactions = queryParameters.isNumberOfTransactions(); final boolean isTransactionAmount = queryParameters.isTransactionAmount(); final boolean isAds = queryParameters.isNumberOfAds(); final StatisticalDTO dto = new StatisticalDTO(); dto.setGroups(groups); dto.setPaymentFilter(paymentFilter); dto.setPeriod(totalPeriod); final List<KeyDevelopmentsStatsPerMonthVO> grossProductPerMonth = isGrossProduct ? getTransferDao().getGrossProductPerMonth(dto) : null; final List<KeyDevelopmentsStatsPerMonthVO> numberOfTransactionsPerMonth = isNumberOfTransactions ? getTransferDao().getNumberOfTransactionsPerMonth(dto) : null; int rowIndex = 0; for (final Period period : periods) { int columnIndex = 0; if (throughTimeRange == ThroughTimeRange.YEAR) { rowHeaders[rowIndex] = "" + period.getBegin().get(Calendar.YEAR); } else if (throughTimeRange == ThroughTimeRange.QUARTER) { rowHeaders[rowIndex] = "" + period.getBegin().get(Calendar.YEAR) + " - " + period.getBeginQuarter().toStringRepresentation(); } else { rowHeaders[rowIndex] = period.getBegin().get(Calendar.YEAR) + "-" + (period.getBegin().get(Calendar.MONTH) + 1 >= 10 ? period.getBegin().get(Calendar.MONTH) + 1 : "0" + (period.getBegin().get(Calendar.MONTH) + 1)); } if (isNumberOfMembers) { final int periodMainMembersCount = getElementDao().getNumberOfMembersInGroupsInPeriod(groups, period); tableCells[rowIndex][columnIndex] = new StatisticalNumber(periodMainMembersCount); columnIndex++; } if (isGrossProduct) { final Double grossProduct = periodize(grossProductPerMonth, period); tableCells[rowIndex][columnIndex] = new StatisticalNumber(grossProduct.doubleValue(), precision); columnIndex++; } if (isNumberOfTransactions) { final Double numberOfTransactions = periodize(numberOfTransactionsPerMonth, period); tableCells[rowIndex][columnIndex] = new StatisticalNumber(numberOfTransactions); columnIndex++; } if (isTransactionAmount) { final StatisticalDTO tempDto = new StatisticalDTO(); tempDto.setGroups(groups); tempDto.setPaymentFilter(paymentFilter); tempDto.setPeriod(period); final List<Number> transactionAmounts = getTransferDao().getTransactionAmounts(tempDto); tableCells[rowIndex][columnIndex] = Median.getMedian(transactionAmounts, StatisticalService.ALPHA); columnIndex++; } if (isAds) { final Integer activeAdsPeriodMain = adDao.getNumberOfAds(period.getEnd(), groups, Ad.Status.ACTIVE); tableCells[rowIndex][columnIndex++] = new StatisticalNumber(activeAdsPeriodMain); } rowIndex++; } return tableCells; } /** * Contains the decision logic on how to retrieve a list with all relevant transactionAmounts. Does this by checking if a list was already * retrieved. If so, it uses the saved list; if not, it calls getTransactionAmountsFromDatabase. * @param queryParameters * @param paymentFilter * @return a list with all relevant transactionAmounts. */ private List<Number> getTransactionAmounts(final StatisticalKeyDevelopmentsQuery queryParameters, final PaymentFilter paymentFilter) { if (queryParameters.equals(savedParameters) && transactionAmounts != null) { // if a list was already retrieved, use that return transactionAmounts; } final Period periodMain = queryParameters.getPeriodMain(); final Collection<? extends Group> groups = queryParameters.getGroups(); final List<Number> amounts = getTransactionAmountsFromDatabase(periodMain, groups, paymentFilter); savedParameters = queryParameters; transactionAmounts = amounts; return amounts; } private List<Number> getTransactionAmountsComparedTo(final StatisticalKeyDevelopmentsQuery queryParameters, final PaymentFilter paymentFilter) { if (queryParameters.equals(savedParameters) && transactionAmounts2 != null) { // if a list was already retrieved, use that return transactionAmounts2; } final Period period = queryParameters.getPeriodComparedTo(); final Collection<? extends Group> groups = queryParameters.getGroups(); final List<Number> amounts = getTransactionAmountsFromDatabase(period, groups, paymentFilter); savedParameters = queryParameters; transactionAmounts2 = amounts; return amounts; } /** * gets the transactionAmounts from the database via TransferDao. Don't call directly; Call getTransactionAmounts because this checks if there are * cached lists to use, in stead of doing a very heavy database query. */ private List<Number> getTransactionAmountsFromDatabase(final Period period, final Collection<? extends Group> groups, final PaymentFilter paymentFilter) { final StatisticalDTO dto = new StatisticalDTO(); dto.setPeriod(period); dto.setGroups(groups); dto.setPaymentFilter(paymentFilter); final List<Number> list = getTransferDao().getTransactionAmounts(dto); return list; } /** * passes the currency for Compared Periods as columnSubHeaders to columns with indexes 0 and 1, and as y-axis units * @param result * @param queryParameters */ private void passCurrencyCompared(final StatisticalResultDTO result, final StatisticalKeyDevelopmentsQuery queryParameters) { final Currency currency = getCurrency(queryParameters); result.setColumnSubHeaders(new String[] { parenthesizeString(currency.getSymbol()), parenthesizeString(currency.getSymbol()), "" }); result.setYAxisUnits(currency.getSymbol()); } /** * passes the currency for Single Periods as columnSubHeaders to column with index 0 and as y-axis units * @param result * @param queryParameters */ private void passCurrencySingle(final StatisticalResultDTO result, final StatisticalKeyDevelopmentsQuery queryParameters) { final Currency currency = getCurrency(queryParameters); result.setColumnSubHeaders(new String[] { parenthesizeString(currency.getSymbol()) }); } /** * gets the data (which may be number of members, number of transactions, gross product, transaction amounts, etc) for one period for through time * from the big list with the data for all periods. Summarizes several months together if necessary. * * @param grossProductPerMonth, a <code>KeyDevelopmentsStatsPerMonthVO</code>, which is a wrapper aroumnd the data per month. * @param period * @return the summarized data. */ private double periodize(final List<KeyDevelopmentsStatsPerMonthVO> grossProductPerMonthList, final Period period) { final int year = period.getBegin().get(Calendar.YEAR); final int startMonth = period.getBegin().get(Calendar.MONTH) + 1; final int endMonth = period.getEnd().get(Calendar.MONTH) + 1; double result = 0.0; for (final KeyDevelopmentsStatsPerMonthVO monthData : grossProductPerMonthList) { if (monthData.getMonth() >= startMonth && monthData.getMonth() <= endMonth && monthData.getYear() == year) { result += monthData.getDataField().doubleValue(); } } return result; } }