com.opensourcestrategies.activities.reports.ActivitiesChartsService.java Source code

Java tutorial

Introduction

Here is the source code for com.opensourcestrategies.activities.reports.ActivitiesChartsService.java

Source

/*
 * Copyright (c) Open Source Strategies, Inc.
 *
 * Opentaps is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Opentaps 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Opentaps.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.opensourcestrategies.activities.reports;

import java.awt.Color;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.servlet.ServletUtilities;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.ui.RectangleInsets;
import org.ofbiz.base.util.Debug;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.condition.EntityCondition;
import org.ofbiz.entity.condition.EntityOperator;
import org.opentaps.base.constants.OpentapsConfigurationTypeConstants;
import org.opentaps.base.constants.RoleTypeConstants;
import org.opentaps.base.entities.ActivityFact;
import org.opentaps.base.entities.PartyRole;
import org.opentaps.common.reporting.etl.UtilEtl;
import org.opentaps.domain.DomainService;
import org.opentaps.domain.activities.ActivityFactRepositoryInterface;
import org.opentaps.domain.party.PartyRepositoryInterface;
import org.opentaps.foundation.entity.Entity;
import org.opentaps.foundation.infrastructure.Infrastructure;
import org.opentaps.foundation.infrastructure.InfrastructureException;
import org.opentaps.foundation.infrastructure.User;
import org.opentaps.foundation.repository.RepositoryException;
import org.opentaps.foundation.service.ServiceException;

/**
 * Charts for Activities generated by the JFree API.
 */
public class ActivitiesChartsService extends DomainService {

    private static String MODULE = ActivitiesChartsService.class.getName();

    /** Default width of the generated charts. */
    public static final int CHART_WIDTH = 400;
    /** Default height of the generated charts. */
    public static final int CHART_HEIGHT = 300;

    // input parameters
    private int chartWidth = CHART_WIDTH;
    private int chartHeight = CHART_HEIGHT;
    private int cutoffDays = 10;
    private Set<String> allowedLeadPartyIds;
    private boolean createChart = true;

    // output parameters
    private String chartFileName;
    private Set<String> oldPartyIds;
    private Set<String> recentPartyIds;
    private Set<String> noActivityPartyIds;

    /**
     * Default constructor.
     */
    public ActivitiesChartsService() {
        super();
    }

    /**
     * Creates a new <code>ActivitiesChartsService</code> instance.
     * @param infrastructure an <code>Infrastructure</code> value
     * @param user an <code>User</code> value
     * @param locale a <code>Locale</code> value
     * @exception ServiceException if an error occurs
     */
    public ActivitiesChartsService(Infrastructure infrastructure, User user, Locale locale)
            throws ServiceException {
        super(infrastructure, user, locale);
    }

    // Input parameters

    /**
     * Sets if the chart image should be generated, defaults to <code>true</code>.
     * @param createChart a <code>boolean</code> value
     */
    public void setCreateChart(boolean createChart) {
        this.createChart = createChart;
    }

    /**
     * Sets the width of the chart to generate, defaults to {@link #CHART_WIDTH}.
     * @param chartWidth an <code>int</code> value
     */
    public void setChartWidth(int chartWidth) {
        this.chartWidth = chartWidth;
    }

    /**
     * Sets the width of the chart to generate, defaults to {@link #CHART_WIDTH}.
     * @param chartHeight an <code>int</code> value
     */
    public void setChartHeight(int chartHeight) {
        this.chartHeight = chartHeight;
    }

    /**
     * Sets the cutoffDays, defaults to 10.
     * @param cutoffDays an <code>int</code> value
     */
    public void setCutoffDays(int cutoffDays) {
        this.cutoffDays = cutoffDays;
    }

    /**
     * Sets the lead party ids that are allowed to be accounted in the charts, default to null which accounts all leads, defaults to null.
     * @param allowedLeadPartyIds a <code>Set</code> value
     */
    public void setAllowedLeadPartyIds(Set<String> allowedLeadPartyIds) {
        this.allowedLeadPartyIds = allowedLeadPartyIds;
    }

    // Output parameters

    /**
     * Gets the width of the generated chart.
     * @return an <code>int</code> value
     */
    public int getChartWidth() {
        return chartWidth;
    }

    /**
     * Gets the height of the generated chart.
     * @return an <code>int</code> value
     */
    public int getChartHeight() {
        return chartHeight;
    }

    /**
     * Gets the generated chart file name.
     * @return a <code>String</code> value
     */
    public String getChartFileName() {
        return chartFileName;
    }

    /**
     * Gets the set of lead party id that are in the Old category of the chart.
     * @return a <code>Set<String></code> value
     */
    public Set<String> getOldLeadPartyIds() {
        return oldPartyIds;
    }

    /**
     * Gets the set of lead party id that are in the Recent category of the chart.
     * @return a <code>Set<String></code> value
     */
    public Set<String> getRecentLeadPartyIds() {
        return recentPartyIds;
    }

    /**
     * Gets the set of lead party id that are in the No Activity category of the chart.
     * @return a <code>Set<String></code> value
     */
    public Set<String> getNoActivityLeadPartyIds() {
        return noActivityPartyIds;
    }

    // Service methods

    /**
     * Gets the breakdown of Lead according to their last activity, in Recent / Old / No activity categories, without creating a chart image.
     * @exception ServiceException if an error occurs
     */
    public void getActivitiesByLeadSnapshot() throws ServiceException {
        setCreateChart(false);
        createActivitiesByLeadSnapshotChart();
    }

    /**
     * Snapshot chart that shows the breakdown of Leads according to their last activity, in Recent / Old / No activity categories.
     * @exception ServiceException if an error occurs
     */
    public void createActivitiesByLeadSnapshotChart() throws ServiceException {
        try {
            PartyRepositoryInterface rep = getDomainsDirectory().getPartyDomain().getPartyRepository();
            ActivityFactRepositoryInterface activityFactRepo = getDomainsDirectory().getActivitiesDomain()
                    .getActivityFactRepository();

            // Get date dimension ID according to the cutoff
            Long readingDateDimId = lookupDateDimIdForCutoff();

            activityFactRepo.setDateDimensionId(readingDateDimId);
            activityFactRepo.setAllowedTargetPartyIds(allowedLeadPartyIds);
            activityFactRepo.setTargetRoleTypeId(RoleTypeConstants.LEAD);

            // Get the ActivityFacts grouped by Lead
            Map<String, List<ActivityFact>> facts = activityFactRepo
                    .findLeadsActivitiesGroupedBy(ActivityFact.Fields.targetPartyId);

            createActivitiesSnapshotChartFromGroupedActivities(expandLabel("ActivitiesLeadBreakdown"), facts,
                    readingDateDimId, rep);
        } catch (RepositoryException e) {
            throw new ServiceException(e);
        }
    }

    /**
     * Gets the breakdown of Sales Reps according to their last activity, in Recent / Old / No activity categories, without creating a chart image.
     * @exception ServiceException if an error occurs
     */
    public void getActivitiesBySalesRepSnapshot() throws ServiceException {
        setCreateChart(false);
        createActivitiesBySalesRepSnapshotChart();
    }

    /**
     * Snapshot chart that shows the breakdown of Sales Reps according to their last activity, in Recent / Old / No activity categories.
     * @exception ServiceException if an error occurs
     */
    public void createActivitiesBySalesRepSnapshotChart() throws ServiceException {
        try {
            PartyRepositoryInterface rep = getDomainsDirectory().getPartyDomain().getPartyRepository();
            ActivityFactRepositoryInterface activityFactRepo = getDomainsDirectory().getActivitiesDomain()
                    .getActivityFactRepository();

            // Get date dimension ID according to the cutoff
            Long readingDateDimId = lookupDateDimIdForCutoff();

            activityFactRepo.setDateDimensionId(readingDateDimId);
            activityFactRepo.setAllowedTargetPartyIds(allowedLeadPartyIds);
            activityFactRepo.setTargetRoleTypeId(RoleTypeConstants.LEAD);

            // Get the ActivityFacts grouped by Sales Representative
            Map<String, List<ActivityFact>> facts = activityFactRepo
                    .findLeadsActivitiesGroupedBy(ActivityFact.Fields.teamMemberPartyId);

            createActivitiesSnapshotChartFromGroupedActivities(expandLabel("ActivitiesSalesRepBreakdown"), facts,
                    readingDateDimId, rep);
        } catch (RepositoryException e) {
            throw new ServiceException(e);
        }
    }

    private void createActivitiesSnapshotChartFromGroupedActivities(String chartTitle,
            Map<String, List<ActivityFact>> facts, Long readingDateDimId, PartyRepositoryInterface rep)
            throws ServiceException {
        try {
            // Get totals of old, recent and no activity leads
            oldPartyIds = new TreeSet<String>();
            recentPartyIds = new TreeSet<String>();
            noActivityPartyIds = new TreeSet<String>();
            // get all leads
            noActivityPartyIds.addAll(findAllLeadIds(allowedLeadPartyIds, rep));

            // activities are sorted by dateDimId desc, so we can break early
            for (String teamMemberPartyId : facts.keySet()) {
                List<ActivityFact> activities = facts.get(teamMemberPartyId);

                for (ActivityFact fact : activities) {
                    Debug.logInfo(
                            "Removing [" + teamMemberPartyId + "] from no activities [" + noActivityPartyIds + "]",
                            MODULE);
                    noActivityPartyIds.remove(teamMemberPartyId);
                    if (fact.getDateDimId() < readingDateDimId) {
                        oldPartyIds.add(teamMemberPartyId);
                    } else {
                        recentPartyIds.add(teamMemberPartyId);
                    }
                    break;
                }
            }

            // make sure there is no double accounting of recent leads in old
            oldPartyIds.removeAll(recentPartyIds);

            if (createChart) {
                chartFileName = createRONPieChart(chartTitle, recentPartyIds.size(), oldPartyIds.size(),
                        noActivityPartyIds.size());
            }

        } catch (RepositoryException e) {
            throw new ServiceException(e);
        } catch (InfrastructureException e) {
            throw new ServiceException(e);
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }

    private String createRONPieChart(String title, Number recentCount, Number oldCount, Number noActivityCount)
            throws InfrastructureException, IOException {
        final DefaultPieDataset dataset = new DefaultPieDataset();
        dataset.setValue(expandLabel("ActivitiesRecent"), recentCount);
        dataset.setValue(expandLabel("ActivitiesOlder"), oldCount);
        dataset.setValue(expandLabel("ActivitiesNoActivity"), noActivityCount);
        return createPieChart(dataset, title);
    }

    private String createPieChart(DefaultPieDataset dataset, String title)
            throws InfrastructureException, IOException {
        Debug.logInfo("Charting dashboard [" + title + "]", MODULE);
        // set up the chart
        JFreeChart chart = ChartFactory.createPieChart(title, dataset, true, // include legend
                true, // tooltips
                false // urls
        );
        chart.setBackgroundPaint(Color.white);
        chart.setBorderVisible(true);
        chart.setPadding(new RectangleInsets(5.0, 5.0, 5.0, 5.0));

        // get a reference to the plot for further customization...
        final PiePlot plot = (PiePlot) chart.getPlot();
        plot.setBackgroundPaint(Color.white);
        plot.setCircular(true);
        plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}: {1} / {2}",
                NumberFormat.getNumberInstance(), NumberFormat.getPercentInstance()));
        plot.setNoDataMessage("No data available");

        Color[] colors = {
                Color.decode("#" + infrastructure.getConfigurationValue(
                        OpentapsConfigurationTypeConstants.ACTIVITIES_DASHBOARD_LEADS_NEW_COLOR)),
                Color.decode("#" + infrastructure.getConfigurationValue(
                        OpentapsConfigurationTypeConstants.ACTIVITIES_DASHBOARD_LEADS_OLD_COLOR)),
                Color.decode("#" + infrastructure.getConfigurationValue(
                        OpentapsConfigurationTypeConstants.ACTIVITIES_DASHBOARD_LEADS_NO_ACTIVITY_COLOR)) };
        for (int i = 0; i < dataset.getItemCount(); i++) {
            Comparable<?> key = dataset.getKey(i);
            plot.setSectionPaint(key, colors[i]);
        }

        // save as a png and return the file name
        return ServletUtilities.saveChartAsPNG(chart, chartWidth, chartHeight, null);
    }

    private Timestamp getTimestampFromCutoffDays(int cutoffDays) {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -cutoffDays);
        return new Timestamp(cal.getTime().getTime());
    }

    private Long lookupDateDimIdForCutoff() throws ServiceException {
        try {
            return UtilEtl.lookupDateDimensionForTimestamp(getTimestampFromCutoffDays(cutoffDays),
                    infrastructure.getDelegator());
        } catch (GenericEntityException e) {
            throw new ServiceException(e);
        }
    }

    private Set<String> findAllLeadIds(Set<String> allowedLeadPartyIds, PartyRepositoryInterface repository)
            throws RepositoryException {
        EntityCondition condition = EntityCondition.makeCondition(PartyRole.Fields.roleTypeId.name(),
                RoleTypeConstants.PROSPECT);
        if (allowedLeadPartyIds != null) {
            condition = EntityCondition.makeCondition(condition, EntityCondition
                    .makeCondition(PartyRole.Fields.partyId.name(), EntityOperator.IN, allowedLeadPartyIds));
        }

        return Entity.getDistinctFieldValues(String.class, repository.findList(PartyRole.class, condition,
                Arrays.asList(PartyRole.Fields.partyId.name()), Arrays.asList(PartyRole.Fields.partyId.asc())),
                PartyRole.Fields.partyId);
    }
}