/*
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.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nl.strohalm.cyclos.dao.access.LoginHistoryDAO;
import nl.strohalm.cyclos.entities.Entity;
import nl.strohalm.cyclos.entities.access.User;
import nl.strohalm.cyclos.entities.accounts.Currency;
import nl.strohalm.cyclos.entities.groups.Group;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.entities.reports.StatisticalActivityQuery;
import nl.strohalm.cyclos.entities.reports.StatisticalNumber;
import nl.strohalm.cyclos.entities.reports.StatisticalQuery;
import nl.strohalm.cyclos.entities.reports.StatisticsWhatToShow;
import nl.strohalm.cyclos.services.stats.activity.GrossProductPerMemberStats;
import nl.strohalm.cyclos.services.stats.activity.LoginTimesPerMemberStats;
import nl.strohalm.cyclos.services.stats.activity.TransactionCountPerMemberStats;
import nl.strohalm.cyclos.utils.Pair;
import nl.strohalm.cyclos.utils.Period;
import nl.strohalm.cyclos.utils.statistics.ListOperations;
import nl.strohalm.cyclos.utils.statistics.Median;
/**
* An implementation of the StatisticalActivityService interface; subclasses StatisticalService.
* @author rinke, jefferson, jeancarlo
*/
public class StatisticalActivityServiceImpl extends StatisticalServiceImpl implements StatisticalActivityServiceLocal {
private LoginHistoryDAO loginHistoryDao;
/**
* a cached Map with the number of members per group per period. The key is the period.toString(), the value is the number of members in that
* period.
*/
private Map<String, Integer> numberOfMembersPerGroupPerPeriod;
/**
* cached statisticalQuery. Should not have changed when using numberOfMembersPerGroupPerPeriod.
*/
private StatisticalQuery cachedQueryParameters;
/**
* Gets the results table for comparing the gross product for two periods
*
* @param queryParameters
* @return a StatisticalResultDTO containing the median and n for gross products of members for two different periods.
*/
@Override
public StatisticalResultDTO getComparePeriodsGrossProduct(final StatisticalActivityQuery queryParameters) {
final Period periodMain = queryParameters.getPeriodMain();
final Period periodComparedTo = queryParameters.getPeriodComparedTo();
getInitializedPaymentFilter(queryParameters);
// Assign lists main period
final GrossProductPerMemberStats statsMainPeriod = new GrossProductPerMemberStats(queryParameters, periodMain, getTransferDao());
final double[] periodMainSumOfTraders = statsMainPeriod.getGrossProductPerTradingMember();
final int periodMainNoOfTraders = periodMainSumOfTraders.length;
final double[] periodMainSumOfMembers = statsMainPeriod.getVolumePerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodMain));
final int periodMainNoOfMembers = periodMainSumOfMembers.length;
// Assign lists compared to period
final GrossProductPerMemberStats statsCompared = new GrossProductPerMemberStats(queryParameters, periodComparedTo, getTransferDao());
final double[] periodComparedSumOfTraders = statsCompared.getGrossProductPerTradingMember();
final int periodComparedNofTraders = periodComparedSumOfTraders.length;
final double[] periodComparedSumOfMembers = statsCompared.getVolumePerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodComparedTo));
final int periodComparedNofMembers = periodComparedSumOfMembers.length;
// Initialize
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.comparePeriods.grossProduct";
// Assignment of results
if (periodMainNoOfTraders >= MINIMUM_NUMBER_OF_VALUES || periodComparedNofTraders >= MINIMUM_NUMBER_OF_VALUES || periodMainNoOfMembers >= MINIMUM_NUMBER_OF_VALUES || periodComparedNofMembers >= MINIMUM_NUMBER_OF_VALUES) {
final StatisticalNumber periodMainMedianGrossProductPerTrader = Median.getMedian(periodMainSumOfTraders, StatisticalService.ALPHA);
final StatisticalNumber periodComparedToMedianGrossProductPerTrader = Median.getMedian(periodComparedSumOfTraders, StatisticalService.ALPHA);
final StatisticalNumber periodMainMedianGrossProductPerMember = Median.getMedian(periodMainSumOfMembers, StatisticalService.ALPHA);
final StatisticalNumber periodComparedToMedianGrossProductPerMember = Median.getMedian(periodComparedSumOfMembers, StatisticalService.ALPHA);
final StatisticalNumber pTraders = StatisticalServiceImpl.calculatePvalue(periodMainSumOfTraders, periodComparedSumOfTraders);
final StatisticalNumber pAllMembers = StatisticalServiceImpl.calculatePvalue(periodMainSumOfMembers, periodComparedSumOfMembers);
// Assign table values
final Number[][] tableCells = new Number[2][6];
tableCells[0][0] = periodComparedToMedianGrossProductPerTrader;
tableCells[0][1] = periodMainMedianGrossProductPerTrader;
tableCells[0][2] = new StatisticalNumber(periodComparedNofTraders);
tableCells[0][3] = new StatisticalNumber(periodMainNoOfTraders);
tableCells[0][4] = StatisticalNumber.createPercentage(periodMainMedianGrossProductPerTrader, periodComparedToMedianGrossProductPerTrader);
tableCells[0][5] = pTraders;
tableCells[1][0] = periodComparedToMedianGrossProductPerMember;
tableCells[1][1] = periodMainMedianGrossProductPerMember;
tableCells[1][2] = new StatisticalNumber(periodComparedNofMembers);
tableCells[1][3] = new StatisticalNumber(periodMainNoOfMembers);
tableCells[1][4] = StatisticalNumber.createPercentage(periodMainMedianGrossProductPerMember, periodComparedToMedianGrossProductPerMember);
tableCells[1][5] = pAllMembers;
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
setGeneralsForCompare2Periods(result, queryParameters, 2);
// set the currency subHeaders AFTER setGenerals, because subHeaders are overwritten again
final Currency currency = getCurrency(queryParameters);
final String[] subHeaders = new String[] { parenthesizeString(currency.getSymbol()), parenthesizeString(currency.getSymbol()), queryParameters.getPeriodComparedTo().getName(), queryParameters.getPeriodMain().getName(), "", "" };
result.setColumnSubHeaders(subHeaders);
result.setYAxisUnits(currency.getSymbol());
if (queryParameters.isGrossProductGraph()) {
result.setGraphDimensions(null, 2, null);
result.setGraphType(StatisticalResultDTO.GraphType.BAR);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getComparePeriodsLoginTimes(final StatisticalActivityQuery queryParameters) {
final Period periodMain = queryParameters.getPeriodMain();
final Period periodComparedTo = queryParameters.getPeriodComparedTo();
// Main period
final LoginTimesPerMemberStats statsMainPeriod = new LoginTimesPerMemberStats(queryParameters, periodMain, loginHistoryDao);
final List<Number> periodMainLoginTimes = statsMainPeriod.getListLoginTimes();
final int periodMainNoOfMembers = periodMainLoginTimes.size();
// Compared to period
final LoginTimesPerMemberStats statsComparedPeriod = new LoginTimesPerMemberStats(queryParameters, periodComparedTo, loginHistoryDao);
final List<Number> periodComparedLoginTimes = statsComparedPeriod.getListLoginTimes();
final int periodComparedNoOfMembers = periodComparedLoginTimes.size();
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.comparePeriods.loginTimes";
if (periodMainNoOfMembers >= MINIMUM_NUMBER_OF_VALUES || periodComparedNoOfMembers >= MINIMUM_NUMBER_OF_VALUES) {
final StatisticalNumber periodMainMedianLoginTimesPerMember = Median.getMedian(periodMainLoginTimes, StatisticalService.ALPHA);
final StatisticalNumber periodComparedToMedianLoginTimesPerMember = Median.getMedian(periodComparedLoginTimes, StatisticalService.ALPHA);
final StatisticalNumber pValue = StatisticalServiceImpl.calculatePvalue(periodMainLoginTimes, periodComparedLoginTimes);
final Number[][] tableCells = new Number[1][4];
tableCells[0][1] = periodMainMedianLoginTimesPerMember;
tableCells[0][0] = periodComparedToMedianLoginTimesPerMember;
tableCells[0][2] = StatisticalNumber.createPercentage(periodMainMedianLoginTimesPerMember, periodComparedToMedianLoginTimesPerMember);
tableCells[0][3] = pValue;
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
setGeneralsForCompare2Periods(result, queryParameters, 1);
final String[] columnSubHeaders = { "(n=" + periodComparedNoOfMembers + ")", "(n=" + periodMainNoOfMembers + ")", "", "" };
result.setColumnSubHeaders(columnSubHeaders);
if (queryParameters.isLoginTimesGraph()) {
result.setGraphDimensions(null, 2, null);
result.setGraphType(StatisticalResultDTO.GraphType.BAR);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
/**
* Gets the results table for comparing the number of transactions per member for two periods. Transactions are incoming AND outgoing
* transactions, in contrast to grossproduct, which concerns only incoming
*
* @param queryParameters
* @return a StatisticalResultDTO containing the median and n for number of transactions per member for two different periods.
*/
@Override
public StatisticalResultDTO getComparePeriodsNumberTransactions(final StatisticalActivityQuery queryParameters) {
final Period periodMain = queryParameters.getPeriodMain();
final Period periodComparedTo = queryParameters.getPeriodComparedTo();
getInitializedPaymentFilter(queryParameters);
// Main period
final TransactionCountPerMemberStats statsMainPeriod = new TransactionCountPerMemberStats(queryParameters, periodMain, getTransferDao(), getElementDao());
final List<Number> periodMainTransactionCountTraders = statsMainPeriod.getTransactionCountPerTradingMember();
final int periodMainNofTraders = periodMainTransactionCountTraders.size();
final List<Number> periodMainTransactionCountMembers = statsMainPeriod.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodMain));
final int periodMainNofMembers = periodMainTransactionCountMembers.size();
// Compared to period
final TransactionCountPerMemberStats statsCompared = new TransactionCountPerMemberStats(queryParameters, periodComparedTo, getTransferDao(), getElementDao());
final List<Number> periodComparedTransactionCountTraders = statsCompared.getTransactionCountPerTradingMember();
final int periodComparedNofTraders = periodComparedTransactionCountTraders.size();
final List<Number> periodComparedTransactionCountMembers = statsCompared.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodComparedTo));
final int periodComparedNofMembers = periodComparedTransactionCountMembers.size();
// Initialize
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.comparePeriods.numberTransactions";
if (periodMainNofTraders >= MINIMUM_NUMBER_OF_VALUES || periodComparedNofTraders >= MINIMUM_NUMBER_OF_VALUES || periodMainNofMembers >= MINIMUM_NUMBER_OF_VALUES || periodComparedNofMembers >= MINIMUM_NUMBER_OF_VALUES) {
final StatisticalNumber periodMainMedianTransactionCountPerTrader = Median.getMedian(periodMainTransactionCountTraders, StatisticalService.ALPHA);
final StatisticalNumber periodComparedMedianTransactionCountPerTrader = Median.getMedian(periodComparedTransactionCountTraders, StatisticalService.ALPHA);
final StatisticalNumber periodMainMedianTransactionCountPerMember = Median.getMedian(periodMainTransactionCountMembers, StatisticalService.ALPHA);
final StatisticalNumber periodComparedMedianTransactionCountPerMember = Median.getMedian(periodComparedTransactionCountMembers, StatisticalService.ALPHA);
final StatisticalNumber pTraders = StatisticalServiceImpl.calculatePvalue(ListOperations.listToArray(periodMainTransactionCountTraders), ListOperations.listToArray(periodComparedTransactionCountTraders));
final StatisticalNumber pMembers = StatisticalServiceImpl.calculatePvalue(ListOperations.listToArray(periodMainTransactionCountMembers), ListOperations.listToArray(periodComparedTransactionCountMembers));
final Number[][] tableCells = new Number[2][6];
// Row for members which did the transfers
tableCells[0][0] = periodComparedMedianTransactionCountPerTrader;
tableCells[0][1] = periodMainMedianTransactionCountPerTrader;
tableCells[0][2] = periodComparedNofTraders;
tableCells[0][3] = periodMainNofTraders;
tableCells[0][4] = StatisticalNumber.createPercentage(periodMainMedianTransactionCountPerTrader, periodComparedMedianTransactionCountPerTrader);
tableCells[0][5] = pTraders;
// Row for all the members.
tableCells[1][0] = periodComparedMedianTransactionCountPerMember;
tableCells[1][1] = periodMainMedianTransactionCountPerMember;
tableCells[1][2] = periodComparedNofMembers;
tableCells[1][3] = periodMainNofMembers;
tableCells[1][4] = StatisticalNumber.createPercentage(periodMainMedianTransactionCountPerMember, periodComparedMedianTransactionCountPerMember);
tableCells[1][5] = pMembers;
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
setGeneralsForCompare2Periods(result, queryParameters, 2);
if (queryParameters.isNumberTransactionsGraph()) {
result.setGraphDimensions(null, 2, null);
result.setGraphType(StatisticalResultDTO.GraphType.BAR);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
/**
* gets the percentage of members not trading, where trade concerns incoming and outgoing transactions.
*/
@Override
public StatisticalResultDTO getComparePeriodsPercentageNoTrade(final StatisticalActivityQuery queryParameters) {
final Period periodMain = queryParameters.getPeriodMain();
final Period periodComparedTo = queryParameters.getPeriodComparedTo();
getInitializedPaymentFilter(queryParameters);
// Main period
final TransactionCountPerMemberStats statsMainPeriod = new TransactionCountPerMemberStats(queryParameters, periodMain, getTransferDao(), getElementDao());
final int periodMainNoOfTraders = statsMainPeriod.getTransactionCountPerTradingMember().size();
final int periodMainNoOfMembers = statsMainPeriod.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodMain)).size();
// Compared to period
final TransactionCountPerMemberStats statsCompared = new TransactionCountPerMemberStats(queryParameters, periodComparedTo, getTransferDao(), getElementDao());
final int periodComparedNoOfTraders = statsCompared.getTransactionCountPerTradingMember().size();
final int periodComparedNoOfMembers = statsCompared.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodComparedTo)).size();
// Data structure to build the table
final Number[][] tableCells = new Number[1][4];
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.comparePeriods.percentageNoTrade";
if (periodMainNoOfMembers >= StatisticalService.MINIMUM_NUMBER_OF_VALUES || periodComparedNoOfMembers >= StatisticalService.MINIMUM_NUMBER_OF_VALUES) {
final StatisticalNumber percentageNoTradersMain = TransactionCountPerMemberStats.getPercentageNoTraders(periodMainNoOfTraders, periodMainNoOfMembers);
final StatisticalNumber percentageNoTradersCompared = TransactionCountPerMemberStats.getPercentageNoTraders(periodComparedNoOfTraders, periodComparedNoOfMembers);
final StatisticalNumber pValue = StatisticalServiceImpl.calculatePvalue(periodMainNoOfTraders, periodMainNoOfMembers, periodComparedNoOfTraders, periodComparedNoOfMembers);
tableCells[0][0] = percentageNoTradersCompared;
tableCells[0][1] = percentageNoTradersMain;
tableCells[0][2] = StatisticalNumber.createPercentage(percentageNoTradersMain, percentageNoTradersCompared);
tableCells[0][3] = pValue;
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
setGeneralsForCompare2Periods(result, queryParameters, 1);
final String[] columnSubHeaders = { "(n=" + periodComparedNoOfMembers + ")", "(n=" + periodMainNoOfMembers + ")", "", "" };
result.setColumnSubHeaders(columnSubHeaders);
if (queryParameters.isPercentageNoTradeGraph()) {
result.setGraphDimensions(null, 2, null);
result.setGraphType(StatisticalResultDTO.GraphType.BAR);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getHistogramGrossProduct(final StatisticalActivityQuery queryParameters) {
Period period = queryParameters.getPeriodMain();
final GrossProductPerMemberStats statsPeriod = new GrossProductPerMemberStats(queryParameters, period, getTransferDao());
final List<Number> listGrossProductPerAllMembers = statsPeriod.getListVolumePerAllMembers(getNumberOfMembersForPeriod(queryParameters, period));
final HistogramDTOFactory histoFactory = new HistogramDTOFactory(listGrossProductPerAllMembers, getLocalSettings());
final String baseKey = "reports.stats.activity.histogram.grossProduct";
final StatisticalResultDTO result = histoFactory.getResultObject(baseKey);
result.setColumnKeys(new String[] { "reports.stats.activity.histogram.grossProduct.xAxis" });
final Currency currency = getCurrency(queryParameters);
result.setXAxisUnits(currency.getSymbol());
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
return result;
}
@Override
public StatisticalResultDTO getHistogramLoginTimes(final StatisticalActivityQuery queryParameters) {
final LoginTimesPerMemberStats statsPeriod = new LoginTimesPerMemberStats(queryParameters, queryParameters.getPeriodMain(), loginHistoryDao);
final List<Number> listLoginTimes = statsPeriod.getListLoginTimes();
final HistogramDTOFactory histoFactory = new HistogramDTOFactory(listLoginTimes, getLocalSettings());
final String baseKey = "reports.stats.activity.histogram.logins";
final StatisticalResultDTO result = histoFactory.getResultObject(baseKey);
result.setColumnKeys(new String[] { "reports.stats.activity.histogram.logins.xAxis" });
passGroupFilter(result, queryParameters);
return result;
}
@Override
public StatisticalResultDTO getHistogramNumberTransactions(final StatisticalActivityQuery queryParameters) {
Period periodMain = queryParameters.getPeriodMain();
final TransactionCountPerMemberStats statsPeriod = new TransactionCountPerMemberStats(queryParameters, periodMain, getTransferDao(), getElementDao());
final List<Number> transactionCountPerAllMembers = statsPeriod.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodMain));
final HistogramDTOFactory histoFactory = new HistogramDTOFactory(transactionCountPerAllMembers, getLocalSettings());
final String baseKey = "reports.stats.activity.histogram.numberTransactions";
final StatisticalResultDTO result = histoFactory.getResultObject(baseKey);
result.setColumnKeys(new String[] { "reports.stats.activity.histogram.numberTransactions.xAxis" });
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
return result;
}
/**
* Gets the results table for the gross product of a single period
*
* @param queryParameters
* @return a StatisticalResultDTO containing the median gross product and the number of members for a single period.
*/
@Override
public StatisticalResultDTO getSinglePeriodGrossProduct(final StatisticalActivityQuery queryParameters) {
getInitializedPaymentFilter(queryParameters);
// Assign lists
final Period periodMain = queryParameters.getPeriodMain();
final GrossProductPerMemberStats statsMainPeriod = new GrossProductPerMemberStats(queryParameters, periodMain, getTransferDao());
final double[] periodMainSumOfTraders = statsMainPeriod.getGrossProductPerTradingMember();
final int periodMainNofTraders = periodMainSumOfTraders.length;
// Trading members
// All members
final double[] periodMainSumOfMembers = statsMainPeriod.getVolumePerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodMain));
final int periodMainNofMembers = periodMainSumOfMembers.length;
// Initialize
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.singlePeriod.grossProduct";
// Assignment of results
if (periodMainNofTraders >= MINIMUM_NUMBER_OF_VALUES || periodMainNofMembers >= MINIMUM_NUMBER_OF_VALUES) {
final StatisticalNumber periodMainMedianGrossProductPerTrader = Median.getMedian(periodMainSumOfTraders, StatisticalService.ALPHA);
final StatisticalNumber periodMainMedianGrossProductPerMember = Median.getMedian(periodMainSumOfMembers, StatisticalService.ALPHA);
// Assign table values
final Number[][] tableCells = new Number[2][2];
// Trading members
tableCells[0][0] = periodMainMedianGrossProductPerTrader;
tableCells[0][1] = new StatisticalNumber(periodMainNofTraders);
// All members
tableCells[1][0] = periodMainMedianGrossProductPerMember;
tableCells[1][1] = new StatisticalNumber(periodMainNofMembers);
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
setGeneralsForSinglePeriod(result, queryParameters, 2, true);
// set the currency subHeaders AFTER setGenerals, because subHeaders are overwritten again
final Currency currency = getCurrency(queryParameters);
final String[] subHeaders = new String[] { parenthesizeString(currency.getSymbol()), queryParameters.getPeriodMain().getName() };
result.setColumnSubHeaders(subHeaders);
result.setYAxisUnits(currency.getSymbol());
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getSinglePeriodLoginTimes(final StatisticalActivityQuery queryParameters) {
final Period periodMain = queryParameters.getPeriodMain();
final LoginTimesPerMemberStats statsMainPeriod = new LoginTimesPerMemberStats(queryParameters, periodMain, loginHistoryDao);
final List<Number> periodMainLoginTimes = statsMainPeriod.getListLoginTimes();
final int periodMainNoOfMembers = periodMainLoginTimes.size();
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.singlePeriod.loginTimes";
if (periodMainNoOfMembers >= MINIMUM_NUMBER_OF_VALUES) {
final StatisticalNumber periodMainMedianLoginTimesPerMember = Median.getMedian(periodMainLoginTimes, StatisticalService.ALPHA);
final Number[][] tableCells = new Number[1][1];
tableCells[0][0] = periodMainMedianLoginTimesPerMember;
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
setGeneralsForSinglePeriod(result, queryParameters, 1, false);
final String[] columnSubHeaders = { "(n=" + periodMainNoOfMembers + ")" };
result.setColumnSubHeaders(columnSubHeaders);
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
/**
* Gets the results table for the number of transactions per member for a single period
*
* @param queryParameters
* @return a StatisticalResultDTO containing the median and n for number of transactions per member for two different periods.
*/
@Override
public StatisticalResultDTO getSinglePeriodNumberTransactions(final StatisticalActivityQuery queryParameters) {
getInitializedPaymentFilter(queryParameters);
final Period periodMain = queryParameters.getPeriodMain();
final TransactionCountPerMemberStats statsMainPeriod = new TransactionCountPerMemberStats(queryParameters, periodMain, getTransferDao(), getElementDao());
// Trading members
final List<Number> periodMainTransactionCountTraders = statsMainPeriod.getTransactionCountPerTradingMember();
final int periodMainNofTraders = periodMainTransactionCountTraders.size();
// All members
final List<Number> periodMainTransactionCountMembers = statsMainPeriod.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodMain));
final int periodMainNofMembers = periodMainTransactionCountMembers.size();
// Initialize
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.singlePeriod.numberTransactions";
if (periodMainNofTraders >= MINIMUM_NUMBER_OF_VALUES || periodMainNofMembers >= MINIMUM_NUMBER_OF_VALUES) {
// Calculate median
final StatisticalNumber periodMainMedianTransactionCountPerTrader = Median.getMedian(periodMainTransactionCountTraders, StatisticalService.ALPHA);
final StatisticalNumber periodMainMedianTransactionCountPerMember = Median.getMedian(periodMainTransactionCountMembers, StatisticalService.ALPHA);
final Number[][] tableCells = new Number[2][2];
// Row for members which did the transfers
tableCells[0][0] = periodMainMedianTransactionCountPerTrader;
tableCells[0][1] = periodMainNofTraders;
// Row for all the members.
tableCells[1][0] = periodMainMedianTransactionCountPerMember;
tableCells[1][1] = periodMainNofMembers;
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
setGeneralsForSinglePeriod(result, queryParameters, 2, true);
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getSinglePeriodPercentageNoTrade(final StatisticalActivityQuery queryParameters) {
getInitializedPaymentFilter(queryParameters);
final Period periodMain = queryParameters.getPeriodMain();
final TransactionCountPerMemberStats statsMainPeriod = new TransactionCountPerMemberStats(queryParameters, periodMain, getTransferDao(), getElementDao());
final int periodMainNoOfTraders = statsMainPeriod.getTransactionCountPerTradingMember().size();
final int periodMainNoOfMembers = statsMainPeriod.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, periodMain)).size();
// Data structure to build the table
final Number[][] tableCells = new Number[1][1];
StatisticalResultDTO result = null;
final String baseKey = "reports.stats.activity.singlePeriod.percentageNoTrade";
if (periodMainNoOfMembers >= StatisticalService.MINIMUM_NUMBER_OF_VALUES) {
final StatisticalNumber percentageNoTradersMain = TransactionCountPerMemberStats.getPercentageNoTraders(periodMainNoOfTraders, periodMainNoOfMembers);
tableCells[0][0] = percentageNoTradersMain;
result = new StatisticalResultDTO(tableCells);
result.setBaseKey(baseKey);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
setGeneralsForSinglePeriod(result, queryParameters, 1, false);
final String[] columnSubHeaders = { "(n=" + periodMainNoOfMembers + ")" };
result.setColumnSubHeaders(columnSubHeaders);
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getThroughTheTimeGrossProduct(final StatisticalActivityQuery queryParameters) {
getInitializedPaymentFilter(queryParameters);
final Period[] periods = queryParameters.getPeriods();
final String[] rowHeaders = new String[periods.length];
final Number[][] tableData = new Number[periods.length][4];
int countEnoughData = 0;
int index = 0;
for (final Period period : periods) {
final GrossProductPerMemberStats statsPeriod = new GrossProductPerMemberStats(queryParameters, period, getTransferDao());
// Gross product of trading members
final double[] periodSumOfTraders = statsPeriod.getGrossProductPerTradingMember();
final StatisticalNumber periodMedianGrossProductPerTrader = Median.getMedian(periodSumOfTraders, StatisticalService.ALPHA);
// Gross product of all members
final double[] periodSumOfMembers = statsPeriod.getVolumePerAllMembers(getNumberOfMembersForPeriod(queryParameters, period));
final StatisticalNumber periodMedianGrossProductPerMember = Median.getMedian(periodSumOfMembers, StatisticalService.ALPHA);
rowHeaders[index] = getRowHeaders(queryParameters.getThroughTimeRange(), period);
tableData[index][0] = periodMedianGrossProductPerTrader; // trading members
tableData[index][1] = periodMedianGrossProductPerMember; // all members
tableData[index][2] = new StatisticalNumber(periodSumOfTraders.length, (byte) 0);
tableData[index][3] = new StatisticalNumber(periodSumOfMembers.length, (byte) 0);
if (periodMedianGrossProductPerTrader.hasEnoughData()) {
countEnoughData++;
}
index++;
}
final String baseKey = "reports.stats.activity.throughTime.grossProduct";
StatisticalResultDTO result = null;
if (countEnoughData > 1) {
final String[] columnKeys = { "reports.stats.activity.throughTime.grossProduct.col1", "reports.stats.activity.throughTime.grossProduct.col2", "reports.stats.activity.throughTime.grossProduct.col3", // Traders
"reports.stats.activity.throughTime.grossProduct.col4" }; // All
result = new StatisticalResultDTO(tableData);
result.setRowHeaders(rowHeaders);
result.setBaseKey(baseKey);
result.setColumnKeys(columnKeys);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
final Currency currency = getCurrency(queryParameters);
result.setColumnSubHeaders(new String[] { parenthesizeString(currency.getSymbol()), parenthesizeString(currency.getSymbol()), "", "" });
result.setYAxisUnits(currency.getSymbol());
if (queryParameters.isGrossProductGraph()) {
result.setGraphDimensions(null, 2, null);
result.setGraphType(StatisticalResultDTO.GraphType.LINE);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getThroughTheTimeLoginTimes(final StatisticalActivityQuery queryParameters) {
final Period[] periods = queryParameters.getPeriods();
final String[] rowHeaders = new String[periods.length];
final Number[][] tableData = new Number[periods.length][2];
int countEnoughData = 0;
int index = 0;
for (final Period period : periods) {
final LoginTimesPerMemberStats statsPeriod = new LoginTimesPerMemberStats(queryParameters, period, loginHistoryDao);
final List<Number> periodLoginTimesPerMember = statsPeriod.getListLoginTimes();
final StatisticalNumber periodMedianLoginTimesPerMember = Median.getMedian(periodLoginTimesPerMember, StatisticalService.ALPHA);
rowHeaders[index] = getRowHeaders(queryParameters.getThroughTimeRange(), period);
tableData[index][0] = periodMedianLoginTimesPerMember;
tableData[index][1] = new StatisticalNumber(periodLoginTimesPerMember.size(), (byte) 0);
if (periodMedianLoginTimesPerMember.hasEnoughData()) {
countEnoughData++;
}
index++;
}
final String baseKey = "reports.stats.activity.throughTime.loginTimes";
StatisticalResultDTO result = null;
if (countEnoughData > 1) {
final String[] columnKeys = { "reports.stats.activity.throughTime.loginTimes.col1", "" };
result = new StatisticalResultDTO(tableData);
result.setBaseKey(baseKey);
result.setRowHeaders(rowHeaders);
result.setColumnKeys(columnKeys);
result.setColumnHeader("n", 1);
passGroupFilter(result, queryParameters);
if (queryParameters.isLoginTimesGraph()) {
result.setGraphDimensions(null, 1, null);
result.setGraphType(StatisticalResultDTO.GraphType.LINE);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getThroughTheTimeNumberTransactions(final StatisticalActivityQuery queryParameters) {
getInitializedPaymentFilter(queryParameters);
final Period[] periods = queryParameters.getPeriods();
final String[] rowHeaders = new String[periods.length];
final Number[][] tableData = new Number[periods.length][4];
int countEnoughData = 0;
int index = 0;
for (final Period period : periods) {
final TransactionCountPerMemberStats statsPeriod = new TransactionCountPerMemberStats(queryParameters, period, getTransferDao(), getElementDao());
// Transactions count of trading members
final List<Number> periodTransactionsCountTraders = statsPeriod.getTransactionCountPerTradingMember();
final StatisticalNumber periodMedianTransactionsCountPerTrader = Median.getMedian(periodTransactionsCountTraders, StatisticalService.ALPHA);
// Transactions count of all members
final List<Number> periodTransactionsCountMembers = statsPeriod.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, period));
final StatisticalNumber periodMedianTransactionsCountPerMember = Median.getMedian(periodTransactionsCountMembers, StatisticalService.ALPHA);
rowHeaders[index] = getRowHeaders(queryParameters.getThroughTimeRange(), period);
tableData[index][0] = periodMedianTransactionsCountPerTrader;
tableData[index][1] = periodMedianTransactionsCountPerMember;
tableData[index][2] = new StatisticalNumber(periodTransactionsCountTraders.size(), (byte) 0);
tableData[index][3] = new StatisticalNumber(periodTransactionsCountMembers.size(), (byte) 0);
if (periodMedianTransactionsCountPerTrader.hasEnoughData()) {
countEnoughData++;
}
index++;
}
final String[] columnKeys = { "reports.stats.activity.throughTime.numberTransactions.col1", "reports.stats.activity.throughTime.numberTransactions.col2", "reports.stats.activity.throughTime.numberTransactions.col3", // Traders
"reports.stats.activity.throughTime.numberTransactions.col4" }; // All
final String baseKey = "reports.stats.activity.throughTime.numberTransactions";
StatisticalResultDTO result = null;
if (countEnoughData > 1) {
result = new StatisticalResultDTO(tableData);
result.setBaseKey(baseKey);
result.setRowHeaders(rowHeaders);
result.setColumnKeys(columnKeys);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
if (queryParameters.isNumberTransactionsGraph()) {
result.setGraphDimensions(null, 2, null);
result.setGraphType(StatisticalResultDTO.GraphType.LINE);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
@Override
public StatisticalResultDTO getThroughTheTimePercentageNoTrade(final StatisticalActivityQuery queryParameters) {
getInitializedPaymentFilter(queryParameters);
final Period[] periods = queryParameters.getPeriods();
final String[] rowHeaders = new String[periods.length];
final Number[][] tableData = new Number[periods.length][2];
int countEnoughData = 0;
int index = 0;
for (final Period period : periods) {
final TransactionCountPerMemberStats statsPeriod = new TransactionCountPerMemberStats(queryParameters, period, getTransferDao(), getElementDao());
// Number of traders
final int periodNoOfTraders = statsPeriod.getTransactionCountPerTradingMember().size();
// Number of members
final int periodNoOfMembers = statsPeriod.getTransactionCountPerAllMembers(getNumberOfMembersForPeriod(queryParameters, period)).size();
// Calculate the percentage of no traders
final StatisticalNumber percentageOfNoTraders = TransactionCountPerMemberStats.getPercentageNoTraders(periodNoOfTraders, periodNoOfMembers);
// StatisticalNumber representing the total number of members
final StatisticalNumber n = new StatisticalNumber(periodNoOfMembers, (byte) 0);
rowHeaders[index] = getRowHeaders(queryParameters.getThroughTimeRange(), period);
tableData[index][0] = percentageOfNoTraders;
tableData[index][1] = n;
index++;
if (periodNoOfMembers >= StatisticalService.MINIMUM_NUMBER_OF_VALUES) {
countEnoughData++;
}
}
final String[] columnKeys = { "reports.stats.activity.throughTime.percentageNoTrade.col1", "" };
final String baseKey = "reports.stats.activity.throughTime.percentageNoTrade";
StatisticalResultDTO result = null;
if (countEnoughData > 1) {
result = new StatisticalResultDTO(tableData);
result.setBaseKey(baseKey);
result.setRowHeaders(rowHeaders);
result.setColumnKeys(columnKeys);
result.setColumnHeader("n", 1);
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
if (queryParameters.isPercentageNoTradeGraph()) {
result.setGraphDimensions(null, 1, null);
result.setGraphType(StatisticalResultDTO.GraphType.LINE);
}
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
/**
* get a top ten with members with the highest personal grossproduct
*/
@Override
public StatisticalResultDTO getToptenPersonalGrossProduct(final StatisticalActivityQuery queryParameters) {
getInitializedPaymentFilter(queryParameters);
final GrossProductPerMemberStats statsPeriod = new GrossProductPerMemberStats(queryParameters, queryParameters.getPeriodMain(), getTransferDao());
final List<Pair<Member, BigDecimal>> rawDataListFromQueryPair = statsPeriod.getSumOfTransfersPerTrader();
final StatisticalResultDTO result = getTopTenFromPairList(rawDataListFromQueryPair, "reports.stats.activity.topten.grossProduct", queryParameters);
final Currency currency = getCurrency(queryParameters);
result.setColumnSubHeaders(new String[] { parenthesizeString(currency.getSymbol()) });
return result;
}
/**
* get a top ten with members with the highest number of logins.
*/
@Override
public StatisticalResultDTO getToptenPersonalLoginTimes(final StatisticalActivityQuery queryParameters) {
final LoginTimesPerMemberStats statsPeriod = new LoginTimesPerMemberStats(queryParameters, queryParameters.getPeriodMain(), loginHistoryDao);
final List<Pair<User, Number>> rawDataListFromQueryPair = statsPeriod.getLoginTimesPerMemberWithoutZeros();
return getTopTenFromPairList(rawDataListFromQueryPair, "reports.stats.activity.topten.login", queryParameters);
}
/**
* get a top ten with members with the highest number of transactions
*/
@Override
public StatisticalResultDTO getToptenPersonalNumberTransactions(final StatisticalActivityQuery queryParameters) {
final TransactionCountPerMemberStats statsPeriod = new TransactionCountPerMemberStats(queryParameters, queryParameters.getPeriodMain(), getTransferDao(), getElementDao());
final List<Pair<Member, Integer>> rawDataListFromQueryPair = statsPeriod.getTransfersPerTrader();
return getTopTenFromPairList(rawDataListFromQueryPair, "reports.stats.activity.topten.numberTransactions", queryParameters);
}
public void setLoginHistoryDao(final LoginHistoryDAO loginHistoryDao) {
this.loginHistoryDao = loginHistoryDao;
}
/**
* sets the column headers and keys
*
* @param result the result value object
* @param queryParameters
* @param sixColumns
*/
private void applyColumnHeadersAndKeys(final StatisticalResultDTO result, final StatisticalActivityQuery queryParameters, final boolean sixColumns) {
if (sixColumns) {
final String[] columnKeys = { "", "", "", "", "reports.stats.general.growth", "reports.stats.general.p" };
result.setColumnKeys(columnKeys);
result.setColumnHeader("N", 3);
result.setColumnHeader("N", 2);
final String[] columnSubHeaders = { "", "", queryParameters.getPeriodComparedTo().getName(), queryParameters.getPeriodMain().getName(), "", "" };
result.setColumnSubHeaders(columnSubHeaders);
} else {
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);
}
private void applyRowKeys(final StatisticalResultDTO result, final int numberOfRows) {
final ArrayList<String> rowKeyList = new ArrayList<String>(numberOfRows);
for (int i = 1; i <= numberOfRows; i++) {
rowKeyList.add(result.getBaseKey() + ".row" + i);
}
String[] rowKeys = new String[numberOfRows];
rowKeys = rowKeyList.toArray(rowKeys);
result.setRowKeys(rowKeys);
}
private void applySinglePeriodColumnHeadersAndKeys(final StatisticalResultDTO result, final StatisticalActivityQuery queryParameters, final boolean twoColumns) {
if (twoColumns) {
final String[] columnKeys = { "", "" };
result.setColumnKeys(columnKeys);
result.setColumnHeader("N", 1);
final String[] columnSubHeaders = { "", queryParameters.getPeriodMain().getName() };
result.setColumnSubHeaders(columnSubHeaders);
} else {
final String[] columnKeys = { "" };
result.setColumnKeys(columnKeys);
}
result.setColumnHeader(queryParameters.getPeriodMain().getName(), 0);
}
private Integer getNumberOfMembersForPeriod(final StatisticalActivityQuery queryParameters, final Period requestedPeriod) {
// we only have to get it anew if that hasn't been done before with exactly the same queryParameters (hence no overridden
// StatisticalActivityQuery.equals)
if (numberOfMembersPerGroupPerPeriod == null || !cachedQueryParameters.equals(queryParameters)) {
// first, get all the periods from ...
final Collection<Period> periods = new ArrayList<Period>();
// ... through the time periods
if (queryParameters.getWhatToShow() == StatisticsWhatToShow.THROUGH_TIME) {
periods.addAll(Arrays.asList(queryParameters.getPeriods()));
}
// ... compare periods: two periods and ...
if (queryParameters.getWhatToShow() == StatisticsWhatToShow.COMPARE_PERIODS) {
final Period periodMain = queryParameters.getPeriodMain();
final Period periodComparedTo = queryParameters.getPeriodComparedTo();
periods.add(periodMain);
periods.add(periodComparedTo);
}
// (Caution) ... single period or histogram and Top ten -> only MAIN PERIOD.
if (queryParameters.getWhatToShow() == StatisticsWhatToShow.SINGLE_PERIOD || queryParameters.getWhatToShow() == StatisticsWhatToShow.DISTRIBUTION) {
final Period periodMain = queryParameters.getPeriodMain();
periods.add(periodMain);
}
final Collection<Group> groups = queryParameters.getGroups();
numberOfMembersPerGroupPerPeriod = new HashMap<String, Integer>();
// loop to get member numbers
for (final Period period : periods) {
final Integer numberOfMembers = getElementDao().getNumberOfMembersInGroupsInPeriod(groups, period);
numberOfMembersPerGroupPerPeriod.put(period.toString(), numberOfMembers);
}
cachedQueryParameters = queryParameters;
}
// now we are sure the map exists, just request its data
return numberOfMembersPerGroupPerPeriod.get(requestedPeriod.toString());
}
/**
* Common method for getting the personal top ten from a Pair List. Fits for all top tens.
*
* @param <S> The first element Type of the Pair which is in the <code>rawDataPairList</code>. This extends <code>Entity</code> and is
* <code>Member</code> for grossProduct and Number of transactions, and <code>User</code> for logins.
* @param <T> The second element Type of the Pair which is in the <code>rawDataPairList</code>. This type extends <code>Number</code> and contains
* the score of the member.
* @param rawDataPairList. The List with results. The List contains <code>Pair</code>s, where the first element represents the member, and the
* second element the score of that member.
* @param baseKey a String representing the base key for the language resource bundle.
* @param queryParameters a <code>StatisticalActivityQuery</code> containing the parameters of the form.
* @return a <code>StatisticalResultDTO</code> containing all information to build the table in the jsp.
*/
private <S extends Entity, T extends Number> StatisticalResultDTO getTopTenFromPairList(final List<Pair<S, T>> rawDataPairList, final String baseKey, final StatisticalActivityQuery queryParameters) {
int last = 10;
if (rawDataPairList.size() < last) {
last = rawDataPairList.size();
}
// determine if there are any more members beyond number 10 having the same score as number 10
int extraItems = 0;
if (last == 10) {
final List<T> loginTimes = ListOperations.getSecondFromPairCollection(rawDataPairList);
final int lastIndex = loginTimes.lastIndexOf(loginTimes.get(9));
if (lastIndex > 9) {
extraItems = lastIndex - 9;
}
}
final String[] rowHeaders = new String[last + (extraItems > 0 ? 1 : 0)];
final StatisticalResultDTO.ResourceKey[] rowKeys = new StatisticalResultDTO.ResourceKey[last + (extraItems > 0 ? 1 : 0)];
final Number[][] tableData = new Number[last + (extraItems > 0 ? 1 : 0)][1];
for (int i = 0; i < last; i++) {
final Pair<S, T> pair = rawDataPairList.get(i);
tableData[i][0] = pair.getSecond();
rowKeys[i] = new StatisticalResultDTO.ResourceKey("");
final S entity = pair.getFirst();
if (entity instanceof Member) {
rowHeaders[i] = "" + (i + 1) + " - " + ((Member) entity).getName();
} else {
rowHeaders[i] = "" + (i + 1) + " - " + ((User) entity).getUsername();
}
// if there are more members with the same score as number 10, add an extra item saying how many
if (i == 9 && extraItems > 0) {
tableData[10][0] = pair.getSecond();
rowKeys[10] = new StatisticalResultDTO.ResourceKey("reports.stats.activity.topten.andMore", new Object[] { extraItems });
}
}
StatisticalResultDTO result = null;
if (last > 0) {
result = new StatisticalResultDTO(tableData);
result.setBaseKey(baseKey);
result.setRowKeys(rowKeys);
for (int i = 0; i < rowHeaders.length - (extraItems > 0 ? 1 : 0); i++) {
result.setRowHeader(rowHeaders[i], i);
}
result.setColumnKeys(new String[] { baseKey + ".col1" });
passGroupFilter(result, queryParameters);
passPaymentFilter(result, queryParameters);
} else {
result = StatisticalResultDTO.noDataAvailable(baseKey);
}
return result;
}
private void setGeneralsForCompare2Periods(final StatisticalResultDTO result, final StatisticalActivityQuery queryParameters, final int numberOfRows) {
applyRowKeys(result, numberOfRows);
final boolean sixColumns = (numberOfRows == 2);
applyColumnHeadersAndKeys(result, queryParameters, sixColumns);
}
private void setGeneralsForSinglePeriod(final StatisticalResultDTO result, final StatisticalActivityQuery queryParameters, final int numberOfRows, final boolean twoColumns) {
applyRowKeys(result, numberOfRows);
applySinglePeriodColumnHeadersAndKeys(result, queryParameters, twoColumns);
}
}