/*
* Copyright (c) 2011 Lockheed Martin Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eurekastreams.server.action.execution;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.logging.Log;
import org.eurekastreams.commons.actions.ExecutionStrategy;
import org.eurekastreams.commons.actions.context.PrincipalActionContext;
import org.eurekastreams.commons.date.DateDayExtractor;
import org.eurekastreams.commons.date.DayOfWeekStrategy;
import org.eurekastreams.commons.date.WeekdaysInDateRangeStrategy;
import org.eurekastreams.commons.logging.LogFactory;
import org.eurekastreams.server.domain.DailyUsageSummary;
import org.eurekastreams.server.persistence.mappers.DomainMapper;
import org.eurekastreams.server.search.modelview.UsageMetricSummaryDTO;
import org.eurekastreams.server.service.actions.requests.UsageMetricStreamSummaryRequest;
import com.ibm.icu.util.Calendar;
/**
* Execution strategy to get the usage metric information for a stream or for all streams.
*/
public class GetUsageMetricSummaryExecution implements ExecutionStrategy<PrincipalActionContext>
{
/**
* Logger.
*/
private final Log logger = LogFactory.make();
/**
* Mapper to get the summary data for a stream, or all streams.
*/
private final DomainMapper<UsageMetricStreamSummaryRequest, List<DailyUsageSummary>> summaryDataMapper;
/**
* Strategy to get the number of weekdays between two dates.
*/
private final WeekdaysInDateRangeStrategy weekdaysInDateRangeStrategy;
/**
* Strategy to determine if a date is a weekday.
*/
private final DayOfWeekStrategy dayOfWeekStrategy;
/**
* Constructor.
*
* @param inSummaryDataMapper
* mapper to get the summary data for a stream, or all streams
* @param inWeekdaysInDateRangeStrategy
* strategy to get the number of weekdays between two dates
* @param inDayOfWeekStrategy
* strategy to determine if a date is a weekday
*/
public GetUsageMetricSummaryExecution(
final DomainMapper<UsageMetricStreamSummaryRequest, List<DailyUsageSummary>> inSummaryDataMapper,
final WeekdaysInDateRangeStrategy inWeekdaysInDateRangeStrategy,
final DayOfWeekStrategy inDayOfWeekStrategy)
{
summaryDataMapper = inSummaryDataMapper;
weekdaysInDateRangeStrategy = inWeekdaysInDateRangeStrategy;
dayOfWeekStrategy = inDayOfWeekStrategy;
}
/**
* Get the daily usage summary for all streams or for a specific stream.
*
* @param inActionContext
* the action context containing the UsageMetricDailyStreamInfoRequest
* @return the UsageMetricSummaryDTO
*/
@Override
public Serializable execute(final PrincipalActionContext inActionContext)
{
UsageMetricStreamSummaryRequest request = (UsageMetricStreamSummaryRequest) inActionContext.getParams();
List<DailyUsageSummary> results = summaryDataMapper.execute(request);
logger.info("Found " + results.size() + " summary results");
UsageMetricSummaryDTO result = new UsageMetricSummaryDTO();
// can't build the list directly - build up a temporary list so we can fill in the holes later
List<DailyUsageSummary> dailyStats = new ArrayList<DailyUsageSummary>();
// short-circuit if no results.
if (results.size() == 0)
{
return result;
}
long msgCount = 0;
long pageViewCount = 0;
long streamContributorCount = 0;
long streamViewCount = 0;
long streamViewerCount = 0;
long uniqueVisitorCount = 0;
long avgActivityResponseTime = 0;
long startingCommentCount = 0, finalCommentCount = 0;
long startingActivityCount = 0, finalActivityCount = 0;
Long totalActivityCount = null;
Long totalCommentCount = null;
Long totalContributorCount = null;
Calendar day = Calendar.getInstance();
day.add(Calendar.DATE, -request.getNumberOfDays());
Date oldestAllowableReportDate = DateDayExtractor.getStartOfDay(new Date(day.getTimeInMillis()));
day = Calendar.getInstance();
day.add(Calendar.DATE, -1);
Date latestReportDate = DateDayExtractor.getStartOfDay(new Date(day.getTimeInMillis()));
Date summaryDate;
Date oldestAvailableReportDate = null;
Date newestAvailableReportDate = null;
logger.debug("Looking for data between " + oldestAllowableReportDate + " and " + latestReportDate);
for (DailyUsageSummary dus : results)
{
summaryDate = DateDayExtractor.getStartOfDay(dus.getUsageDate());
if (summaryDate.before(oldestAllowableReportDate) || summaryDate.after(latestReportDate))
{
// can't use this data
logger.debug("Can't use data for " + summaryDate);
continue;
}
// set the normalized date, add the date to the list
dus.setUsageDate(summaryDate);
dailyStats.add(dus);
if (newestAvailableReportDate == null || summaryDate.after(newestAvailableReportDate))
{
newestAvailableReportDate = summaryDate;
// this is currently the most recent record - store the totals
totalActivityCount = dus.getTotalActivityCount();
totalCommentCount = dus.getTotalCommentCount();
totalContributorCount = dus.getTotalContributorCount();
finalCommentCount = dus.getTotalCommentCount() == null ? 0 : dus.getTotalCommentCount();
finalActivityCount = dus.getTotalActivityCount() == null ? 0 : dus.getTotalActivityCount();
}
if (oldestAvailableReportDate == null || summaryDate.before(oldestAvailableReportDate))
{
// this is the earliest reporting date we've seen
oldestAvailableReportDate = summaryDate;
startingCommentCount = dus.getTotalCommentCount() == null ? 0 : dus.getTotalCommentCount();
startingActivityCount = dus.getTotalActivityCount() == null ? 0 : dus.getTotalActivityCount();
}
msgCount += dus.getMessageCount();
pageViewCount += dus.getPageViewCount();
streamContributorCount += dus.getStreamContributorCount();
streamViewCount += dus.getStreamViewCount();
streamViewerCount += dus.getStreamViewerCount();
uniqueVisitorCount += dus.getUniqueVisitorCount();
avgActivityResponseTime += dus.getAvgActivityResponseTime();
}
// number of weekdays between the two dates
long weekdaysCount = 0;
if (oldestAvailableReportDate != null)
{
weekdaysCount = weekdaysInDateRangeStrategy.getWeekdayCountBetweenDates(oldestAvailableReportDate,
DateDayExtractor.getStartOfDay(new Date()));
}
result.setWeekdayRecordCount(weekdaysCount);
result.setTotalActivityCount(totalActivityCount);
result.setTotalCommentCount(totalCommentCount);
result.setTotalContributorCount(totalContributorCount);
logger.debug("Found " + weekdaysCount + " weekdays between " + oldestAvailableReportDate + " and "
+ latestReportDate);
if (weekdaysCount > 0)
{
result.setAverageDailyMessageCount(Math.round(Math.ceil(msgCount * 1.0 / weekdaysCount)));
result.setAverageDailyPageViewCount(Math.round(Math.ceil(pageViewCount * 1.0 / weekdaysCount)));
result.setAverageDailyStreamContributorCount(Math.round(Math.ceil(streamContributorCount * 1.0
/ weekdaysCount)));
result.setAverageDailyStreamViewCount(Math.round(Math.ceil(streamViewCount * 1.0 / weekdaysCount)));
result.setAverageDailyStreamViewerCount(Math.round(Math.ceil(streamViewerCount * 1.0 / weekdaysCount)));
result.setAverageDailyUniqueVisitorCount(Math.round(Math.ceil(uniqueVisitorCount * 1.0 / weekdaysCount)));
result.setAverageDailyActivityResponseTime(Math.round(Math.ceil(avgActivityResponseTime * 1.0
/ weekdaysCount)));
long averageCommentPerActivity = 0;
if (weekdaysCount > 1)
{
double top = (finalCommentCount - startingCommentCount) * 1.0;
double bottom = (finalActivityCount - startingActivityCount) * 1.0;
if (bottom > 0)
{
// Note: this is average comments per activity per day = (delta comments)/(delta activities) divided
// by weekdaysCount-1. The -1 is necessary because we calculate this number by subtracting oldest
// day from newest
averageCommentPerActivity = Math.round(Math.ceil(top / (bottom * (weekdaysCount - 1))));
}
}
result.setAverageDailyCommentPerActivityCount(averageCommentPerActivity);
}
result.setDailyStatistics(new ArrayList<DailyUsageSummary>());
if (dailyStats.size() > 0)
{
// Loop through every date from the start of reporting for this stream to now, filling in records for
// weekdays
// that have none to make the client-side logic easier
Calendar cal = Calendar.getInstance();
cal.setTime(oldestAvailableReportDate);
Date reportDate;
long lastDateInMs = latestReportDate.getTime();
int dataIndex = 0; // the current index in the data list
// when looping, include a check to make sure we don't add more records than expected - this is a safeguard
// in case there's an unexpected daylight savings time condition that'll cause an infinite loop
for (reportDate = cal.getTime(); reportDate.getTime() <= lastDateInMs
&& result.getDailyStatistics().size() < weekdaysCount; cal.add(Calendar.DATE, 1), reportDate = cal
.getTime())
{
if (!dayOfWeekStrategy.isWeekday(reportDate))
{
// we don't include weekend data
continue;
}
// it's a weekday - we need a record for this date
if (dataIndex < dailyStats.size())
{
// there's still data records to look for
DailyUsageSummary record = dailyStats.get(dataIndex);
if (DateDayExtractor.getStartOfDay(record.getUsageDate()).getTime() == reportDate.getTime())
{
// this is the data we're looking for - add it to the result list
result.getDailyStatistics().add(record);
// bump up the position index for the next loop iteration
dataIndex++;
}
else
{
// we don't have data for that weekday - add a null to the result list
result.getDailyStatistics().add(null);
}
}
else
{
// no more data to look for - we're just adding nulls until the end
result.getDailyStatistics().add(null);
}
}
}
return result;
}
}