org.goobi.production.flow.statistics.hibernate.StatQuestThroughput.java Source code

Java tutorial

Introduction

Here is the source code for org.goobi.production.flow.statistics.hibernate.StatQuestThroughput.java

Source

/*
 * (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
 *
 * This file is part of the Kitodo project.
 *
 * It is licensed under GNU General Public License version 3 or later.
 *
 * For the full copyright and license information, please read the
 * GPL3-License.txt file that was distributed with this source code.
 */

package org.goobi.production.flow.statistics.hibernate;

import de.intranda.commons.chart.renderer.ChartRenderer;
import de.intranda.commons.chart.renderer.IRenderer;
import de.intranda.commons.chart.results.DataRow;
import de.intranda.commons.chart.results.DataTable;
import de.sub.goobi.helper.Helper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.goobi.production.flow.statistics.IStatisticalQuestion;
import org.goobi.production.flow.statistics.IStatisticalQuestionLimitedTimeframe;
import org.goobi.production.flow.statistics.enums.CalculationUnit;
import org.goobi.production.flow.statistics.enums.StatisticsMode;
import org.goobi.production.flow.statistics.enums.TimeUnit;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.type.StandardBasicTypes;
import org.kitodo.data.database.helper.enums.HistoryTypeEnum;
import org.kitodo.dto.BaseDTO;
import org.kitodo.dto.ProcessDTO;

/**
 * Implementation of {@link IStatisticalQuestion}. Statistical Request with
 * predefined Values in data Table
 * 
 * @author Wulf Riebensahm
 */
public class StatQuestThroughput implements IStatisticalQuestionLimitedTimeframe {

    private Date timeFilterFrom;
    private TimeUnit timeGrouping;
    private Date timeFilterTo;
    private List<Integer> myIDlist;
    private Boolean flagIncludeLoops = false;

    /**
     * loops included means that all step open all stepdone are considered loops not
     * included means that only min(date) or max(date) - depending on option in.
     *
     * @see HistoryTypeEnum
     *
     * @return status of loops included or not
     */
    public Boolean getIncludeLoops() {
        return this.flagIncludeLoops;
    }

    /**
     * Set status of loops included.
     *
     * @param includeLoops
     *            Boolean
     */
    public void setIncludeLoops(Boolean includeLoops) {
        this.flagIncludeLoops = includeLoops;
    }

    private final Logger logger = LogManager.getLogger(StatQuestThroughput.class);

    /*
     * (non-Javadoc)
     *
     * @see org.goobi.production.flow.statistics.IStatisticalQuestion#setTimeUnit
     * (org.goobi.production.flow.statistics.enums.TimeUnit)
     */
    @Override
    public void setTimeUnit(TimeUnit timeGrouping) {
        this.timeGrouping = timeGrouping;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.goobi.production.flow.statistics.IStatisticalQuestion#getDataTables
     * (List)
     */
    @Override
    public List<DataTable> getDataTables(List<? extends BaseDTO> dataSource) {

        List<DataTable> allTables = new ArrayList<>();

        // gathering IDs from the filter passed by dataSource
        this.myIDlist = getIds(dataSource);

        if (myIDlist == null || myIDlist.size() == 0) {
            return null;
        }

        // a list of DataTables is expected as return Object, even if there is
        // only one Data Table as it is here in this implementation
        DataTable tableStepOpenAndDone = getAllSteps(HistoryTypeEnum.taskOpen);
        tableStepOpenAndDone.setUnitLabel(Helper.getTranslation(this.timeGrouping.getSingularTitle()));
        tableStepOpenAndDone.setName(StatisticsMode.getByClassName(this.getClass()).getTitle() + " ("
                + Helper.getTranslation("openSteps") + ")");
        tableStepOpenAndDone = tableStepOpenAndDone.getDataTableInverted();
        tableStepOpenAndDone = tableStepOpenAndDone.getDataTableInverted();
        tableStepOpenAndDone.setShowableInChart(false);
        allTables.add(tableStepOpenAndDone);

        tableStepOpenAndDone = getAllSteps(HistoryTypeEnum.taskDone);
        tableStepOpenAndDone.setUnitLabel(Helper.getTranslation(this.timeGrouping.getSingularTitle()));
        tableStepOpenAndDone.setName(StatisticsMode.getByClassName(this.getClass()).getTitle() + " ("
                + Helper.getTranslation("doneSteps") + ")");
        tableStepOpenAndDone.setShowableInChart(false);
        tableStepOpenAndDone = tableStepOpenAndDone.getDataTableInverted();
        tableStepOpenAndDone = tableStepOpenAndDone.getDataTableInverted();
        allTables.add(tableStepOpenAndDone);

        // what do we do here?
        // okay ... first we find out how many steps the selected set has
        // finding lowest step and highest step (no step name discrimination)
        Integer uBound;
        Integer uBoundOpen = getMaxStepCount(HistoryTypeEnum.taskOpen);
        Integer uBoundDone = getMaxStepCount(HistoryTypeEnum.taskDone);
        if (uBoundOpen < uBoundDone) {
            uBound = uBoundDone;
        } else {
            uBound = uBoundOpen;
        }

        Integer lBound;
        Integer lBoundOpen = getMinStepCount(HistoryTypeEnum.taskOpen);
        Integer lBoundDone = getMinStepCount(HistoryTypeEnum.taskDone);

        if (lBoundOpen < lBoundDone) {
            lBound = lBoundDone;
        } else {
            lBound = lBoundOpen;
        }

        // then for each step we get both the open and the done count within the
        // selected intervalls and merge it within one table
        for (Integer i = lBound; i <= uBound; i++) {

            DataTable tableStepOpen;
            tableStepOpen = getSpecificSteps(i, HistoryTypeEnum.taskOpen);

            tableStepOpen.setShowableInTable(true);

            DataTable tableStepDone;
            tableStepDone = getSpecificSteps(i, HistoryTypeEnum.taskDone);

            tableStepDone.setShowableInTable(true);

            // to merge we just take each table and dump the entire content in a
            // row for the open step
            DataRow rowOpenSteps = new DataRow(Helper.getTranslation("openSteps") + " " + i.toString());
            for (DataRow dtr : tableStepOpen.getDataRows()) {
                rowOpenSteps.addValue(dtr.getName(), dtr.getValue(0));
            }

            // adding the first row
            String title = "";
            if (tableStepOpen.getName().length() > 0) {
                title = tableStepOpen.getName();
            } else {
                title = tableStepDone.getName();
            }

            tableStepOpenAndDone = new DataTable(
                    Helper.getTranslation("throughput") + " " + Helper.getTranslation("steps") + " " + title);
            tableStepOpenAndDone.addDataRow(rowOpenSteps);

            // row for the done step
            rowOpenSteps = new DataRow(Helper.getTranslation("doneSteps") + " " + i.toString());
            for (DataRow dtr : tableStepDone.getDataRows()) {
                rowOpenSteps.addValue(dtr.getName(), dtr.getValue(0));
            }

            // adding that row
            tableStepOpenAndDone.addDataRow(rowOpenSteps);

            // turning off table rendering
            tableStepOpenAndDone.setShowableInTable(false);

            // inverting the orientation
            tableStepOpenAndDone = tableStepOpenAndDone.getDataTableInverted();
            tableStepOpenAndDone.setUnitLabel(Helper.getTranslation(this.timeGrouping.getSingularTitle()));

            // Dates may not be all in the right order because of it's
            // composition from 2 tables
            List<DataRow> allTempRows = tableStepOpenAndDone.getDataRows();
            // this fixes the sorting problem
            Collections.sort(allTempRows, new DataTableComparator());

            allTables.add(tableStepOpenAndDone);
        }

        return allTables;
    }

    @SuppressWarnings("unchecked")
    private List<Integer> getIds(List<? extends BaseDTO> dataSource) {
        List<Integer> ids = new ArrayList<>();
        for (ProcessDTO process : (List<ProcessDTO>) dataSource) {
            ids.add(process.getId());
        }
        return ids;
    }

    private static class DataTableComparator implements Comparator<DataRow> {
        @Override
        public int compare(DataRow firstDataRow, DataRow secondDataRow) {
            return firstDataRow.getName().compareTo(secondDataRow.getName());
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.goobi.production.flow.statistics.IStatisticalQuestion#
     * setCalculationUnit
     * (org.goobi.production.flow.statistics.enums.CalculationUnit)
     */
    @Override
    public void setCalculationUnit(CalculationUnit cu) {
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.goobi.production.flow.statistics.IStatisticalQuestionLimitedTimeframe
     * #setTimeFrame(java.util.Date, java.util.Date)
     */
    @Override
    public void setTimeFrame(Date timeFrom, Date timeTo) {
        this.timeFilterFrom = timeFrom;
        this.timeFilterTo = timeTo;

    }

    /*
     * (non-Javadoc)
     *
     * @see org.goobi.production.flow.statistics.IStatisticalQuestion#
     * isRendererInverted (de.intranda.commons.chart.renderer.IRenderer)
     */
    @Override
    public Boolean isRendererInverted(IRenderer inRenderer) {
        return inRenderer instanceof ChartRenderer;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.goobi.production.flow.statistics.IStatisticalQuestion#
     * getNumberFormatPattern()
     */
    @Override
    public String getNumberFormatPattern() {
        return "#";
    }

    /**
     * returns a DataTable populated with the specified events.
     *
     * @param requestedType
     *            HistoryTypeEnum object
     * @return DataTable object
     */
    private DataTable getAllSteps(HistoryTypeEnum requestedType) {

        // adding time restrictions
        String natSQL = new SQLStepRequestsImprovedDiscrimination(this.timeFilterFrom, this.timeFilterTo,
                this.timeGrouping, this.myIDlist).getSQL(requestedType, null, true, this.flagIncludeLoops);

        // this one is supposed to make sure, that all possible headers will be
        // thrown out in the first row to build header columns
        String headerFromSQL = new SQLStepRequestsImprovedDiscrimination(this.timeFilterFrom, this.timeFilterTo,
                null, this.myIDlist).getSQL(requestedType, null, true, true);

        this.logger.trace(natSQL);
        this.logger.trace(headerFromSQL);

        return buildDataTableFromSQL(natSQL, headerFromSQL);
    }

    /**
     * returns a DataTable populated with the specified events.
     *
     * @param step
     *            Integer
     * @param requestedType
     *            HistoryTypeEnum object
     * @return DataTable object
     */

    private DataTable getSpecificSteps(Integer step, HistoryTypeEnum requestedType) {

        // adding time restrictions
        String natSQL = new SQLStepRequests(this.timeFilterFrom, this.timeFilterTo, this.timeGrouping,
                this.myIDlist).getSQL(requestedType, step, true, this.flagIncludeLoops);

        this.logger.trace(natSQL);

        return buildDataTableFromSQL(natSQL, null);
    }

    /**
     * Method generates a DataTable based on the input SQL. Methods success is
     * depending on a very specific data structure ... so don't use it if you don't
     * exactly understand it
     *
     * @param natSQL
     *            headerFromSQL -> to be used, if headers need to be read in first
     *            in order to get a certain sorting
     * @return DataTable
     */

    // TODO Remove redundant code
    private DataTable buildDataTableFromSQL(String natSQL, String headerFromSQL) {
        Session session = Helper.getHibernateSession();

        // creating header row from headerSQL (gets all columns in one row
        DataRow headerRow = null;
        if (headerFromSQL != null) {
            headerRow = new DataRow(null);
            SQLQuery headerQuery = session.createSQLQuery(headerFromSQL);

            // needs to be there otherwise an exception is thrown
            headerQuery.addScalar("stepCount", StandardBasicTypes.DOUBLE);
            headerQuery.addScalar("stepName", StandardBasicTypes.STRING);
            headerQuery.addScalar("stepOrder", StandardBasicTypes.DOUBLE);
            headerQuery.addScalar("intervall", StandardBasicTypes.STRING);

            @SuppressWarnings("rawtypes")
            List headerList = headerQuery.list();
            for (Object obj : headerList) {
                Object[] objArr = (Object[]) obj;
                try {
                    headerRow.setName(new Converter(objArr[3]).getString() + "");
                    headerRow.addValue(
                            new Converter(new Converter(objArr[2]).getInteger()).getString() + " ("
                                    + new Converter(objArr[1]).getString() + ")",
                            (new Converter(objArr[0]).getDouble()));

                } catch (Exception e) {
                    headerRow.addValue(e.getMessage(), 0.0);
                }
            }
        }

        SQLQuery query = session.createSQLQuery(natSQL);

        // needs to be there otherwise an exception is thrown
        query.addScalar("stepCount", StandardBasicTypes.DOUBLE);
        query.addScalar("stepName", StandardBasicTypes.STRING);
        query.addScalar("stepOrder", StandardBasicTypes.DOUBLE);
        query.addScalar("intervall", StandardBasicTypes.STRING);

        @SuppressWarnings("rawtypes")
        List list = query.list();

        DataTable dtbl = new DataTable("");

        // if headerRow is set then add it to the DataTable to set columns
        // needs to be removed later
        if (headerRow != null) {
            dtbl.addDataRow(headerRow);
        }

        DataRow dataRow = null;

        // each data row comes out as an Array of Objects
        // the only way to extract the data is by knowing
        // in which order they come out

        // checks if intervall has changed which then triggers the start for a
        // new row
        // intervall here is the timeGroup Expression (e.g. "2006/05" or
        // "2006-10-05")
        String observeIntervall = "";

        for (Object obj : list) {
            Object[] objArr = (Object[]) obj;
            try {
                // objArr[3]
                if (!observeIntervall.equals(new Converter(objArr[3]).getString())) {
                    observeIntervall = new Converter(objArr[3]).getString();

                    // row cannot be added before it is filled because the add
                    // process triggers
                    // a testing for header alignement -- this is where we add
                    // it after iterating it first
                    if (dataRow != null) {
                        dtbl.addDataRow(dataRow);
                    }

                    dataRow = new DataRow(null);
                    // setting row name with localized time group and the
                    // date/time extraction based on the group
                    dataRow.setName(new Converter(objArr[3]).getString() + "");
                }
                dataRow.addValue(
                        new Converter(new Converter(objArr[2]).getInteger()).getString() + " ("
                                + new Converter(objArr[1]).getString() + ")",
                        (new Converter(objArr[0]).getDouble()));

            } catch (Exception e) {
                dataRow.addValue(e.getMessage(), 0.0);
            }
        }
        // to add the last row
        if (dataRow != null) {
            dtbl.addDataRow(dataRow);
        }

        // now removing headerRow
        if (headerRow != null) {
            dtbl.removeDataRow(headerRow);
        }

        return dtbl;
    }

    /**
     * method retrieves the highest step order in the requested history range.
     *
     * @param requestedType
     *            HistoryTypeEnum object
     */
    private Integer getMaxStepCount(HistoryTypeEnum requestedType) {

        // adding time restrictions
        String natSQL = new SQLStepRequestsImprovedDiscrimination(this.timeFilterFrom, this.timeFilterTo,
                this.timeGrouping, this.myIDlist).getSQLMaxStepOrder(requestedType);

        Session session = Helper.getHibernateSession();
        SQLQuery query = session.createSQLQuery(natSQL);

        // needs to be there otherwise an exception is thrown
        query.addScalar("maxStep", StandardBasicTypes.DOUBLE);

        @SuppressWarnings("rawtypes")
        List list = query.list();

        if (list != null && list.size() > 0 && list.get(0) != null) {
            return new Converter(list.get(0)).getInteger();
        } else {
            return 0;
        }

    }

    /**
     * method retrieves the lowest step order in the requested history range.
     *
     * @param requestedType
     *            HistoryTypeEnum object
     */
    private Integer getMinStepCount(HistoryTypeEnum requestedType) {
        // adding time restrictions
        String natSQL = new SQLStepRequestsImprovedDiscrimination(this.timeFilterFrom, this.timeFilterTo,
                this.timeGrouping, this.myIDlist).getSQLMinStepOrder(requestedType);

        Session session = Helper.getHibernateSession();
        SQLQuery query = session.createSQLQuery(natSQL);

        // needs to be there otherwise an exception is thrown
        query.addScalar("minStep", StandardBasicTypes.DOUBLE);

        @SuppressWarnings("rawtypes")
        List list = query.list();

        if (list != null && list.size() > 0 && list.get(0) != null) {
            return new Converter(list.get(0)).getInteger();
        } else {
            return 0;
        }

    }

}