Location: PHPKode > projects > Druid Defect Control > defectcontrol_0.9.4/tts/statistics_impl.php
<?php

require_once('database.php');
require_once('errors.php');
 
/*
 * This is the implementation of the statistics renderer. 
 */

/* __ Interface ___________________________________________________________ */

/* These are the allowed graphic types. For each of them, several
 * restrictions and features apply. 
 */
define('TTST_GRAPHTYPE_COLUMNS', 0);
define('TTST_GRAPHTYPE_STACKED_COLUMNS', 1);
define('TTST_GRAPHTYPE_PERCENT_STACKED_COLUMNS', 2);
define('TTST_GRAPHTYPE_BOXES', 3);
define('TTST_GRAPHTYPE_BARS', 4);

define('TEXT_ROW_HEIGHT', 20);
define('COLUMN_WIDTH', 20);
define('Y_SERIE_WIDTH', 70);

$seriesColors = array('#FF6666',
                      '#FFCCCC',
                      '#CCCCFF',
                      '#6666FF',
                      '#009999',
                      '#009900',
                      '#',
                      '#',
                      '#');
                      

/* __ State _______________________________________________________________ */

/* These variables keep track of the render state. Their values should be 
 * set by the appropiate funtions, and will alter the output of the render.
 * All of them should have a default value. Values changed, however, are 
 * preserved between renders in the same page. 
 */

/* Level of grouping needed. Up to 2 levels are supported. 
 * Possible values: 1 - 2
 */
$ttst_numLevels = 1;

$ttst_mainColumn = ""; 
$ttst_childColumn = "";
$ttst_countColumn = "";

$ttst_dataQuery = "";

$ttst_graphicType = TTST_GRAPHTYPE_STACKED_COLUMNS;

/* These are referred to the complete size of the graph, including legend 
 * and all formatting aids. 
 */
$ttst_graphWidth = 700;
$ttst_graphHeight = 200;


/* 
 * Temp assignments, only for development. 
 */
$ttst_numLevels = 2;

$ttst_mainColumn = "month"; 
$ttst_childColumn = "type";
$ttst_countColumn = "count";

$ttst_dataQuery = "select count(d.iid) as count,t.sname as type,".
    "extract(month from d.ddetectdate) as month ".
    "from defect d, defectstatus t where ".
    "d.iiddefectstatus=t.iid ".
    "group by t.sname, month ".
    "order by month";

$ttst_graphicType = TTST_GRAPHTYPE_STACKED_COLUMNS;

$ttst_graphWidth = 400;
$ttst_graphHeight = 200;

/*
 * New test
 */
$ttst_numLevels = 2;

$ttst_mainColumn = "priority"; 
$ttst_childColumn = "type";
$ttst_countColumn = "count";

$ttst_dataQuery = "select count(d.iid), p.sname as priority, ".
                    "t.sname as type FROM defect d, defecttype t, ".
                    "priority p WHERE ".
                    "  d.iidpriority = p.iid AND d.iiddefecttype = t.iid ".
                    " GROUP BY priority,type ORDER BY priority";
$ttst_graphicType = TTST_GRAPHTYPE_STACKED_COLUMNS;

$ttst_graphWidth = 400;
$ttst_graphHeight = 200;


/* __ Implementation ______________________________________________________ */


/* __ Dataset analisys ____________________________________________________ */

/* Sets the elements of $childArray to the different childColumn values 
 * present in the data query. 
 */
function getChildElements($connection, &$childArray)
{
    global $ttst_childColumn;
    global $ttst_dataQuery;
    
    $query = "SELECT DISTINCT($ttst_childColumn) AS distinctfields ".
        "FROM ($ttst_dataQuery) as f";

    $res = ttdb_execQuery($connection, $query);    
    
    while (($fields = ttdb_getArray($res)) != false) {
        $key = $fields['distinctfields'];
        $childArray[$key] = 0;  // init all values to 0
    }    
}

/* Sums all the values of all elements in the given array, and 
 * clears it. 
 */
function getTotalAndClearArray(&$childArray)
{
    $total = 0;
    
    foreach ($childArray as $key => $value) {
        $total += $value;
        $childArray[$key] = 0;
    }
    
    return $total;
}

/* Gets the maximum values that a single mainColumn can reach. 
 * This function can be used for both 1 and 2 level querys. 
 * When used for 1 level queries, childColumn should be the same
 * as mainColumn.
 * TODO: This function can take some significant time in case of long 
 * recordsets, and is a point to optimize. However, normal statistical 
 * querys to be used for graphics will not be very long (otherwise, 
 * the won't fit in the screen anyway)
 */
function getChildLimits($connection, &$childArray, &$maximum, &$numColumns)
{
    global $ttst_mainColumn;
    global $ttst_childColumn;
    global $ttst_dataQuery;
    global $ttst_countColumn;
    $maximum = 0; // will hold the maximum value found so far. 
    $numColumns = 0;
    
    // Issue the query

    $res = ttdb_execQuery($connection, $ttst_dataQuery);
    
    //echo $ttst_dataQuery;
   
    
    // Count values until a different maincolumn value is found. 
    // This is one place where the 'ORDER BY <mainColumn>' requirement is used.
    
    $oldMainColumnValue = "";
    
    $fields = ttdb_getArray($res);

    if (!is_array($fields)) {
        tter_errorWithBackButton("No defects found with the given conditions.");
        exit;     
    }

    do {        
        
        //print_r($fields);
        //echo "Count column: ".$ttst_countColumn.". ";
        $newMainColumnValue = $fields[$ttst_mainColumn];
        
        //echo "new: $newMainColumnValue, old: $oldMainColumnValue<br>";
        
        //print_r($fields); echo "<br>";
        
        if ($oldMainColumnValue != $newMainColumnValue) {          
            // row changed, get the total and prepare it for the next
            $sum = getTotalAndClearArray($childArray);        
            if ($sum > $maximum) 
                $maximum = $sum; 
                
            //echo "sum for '$oldMainColumnValue' = $sum. Max = $maximum<br>";
        
            $oldMainColumnValue = $newMainColumnValue;
            
            // will compensate for the first "fake" change by 
            // not adding the last one. Tricky. 
            $numColumns++;  
        }
            
        $childArray[$fields[$ttst_childColumn]] = $fields[$ttst_countColumn];
        
        //print_r($childArray); echo "<br>";
        
    } while (($fields = ttdb_getArray($res)) != false);

    // last case is not evaluated
    $sum = getTotalAndClearArray($childArray);        
    if ($sum > $maximum) 
        $maximum = $sum; 
        
    //return $maximum;
    
}

/* __ Bars renderer _______________________________________________________ */


/* __ Stacked Columns Render ______________________________________________ */

/* 
 *
 */
function drawColumnData(&$childs, $maxValue, $clientHeight)
{
    global $seriesColors;
    
    echo "<td valign ='bottom' align='center'>\n";
    echo "<table width=\"".COLUMN_WIDTH."\" border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";            
    
    $bgColor = 0;
    foreach ($childs as $key => $value) {
        $currHeight = $clientHeight * $value / $maxValue;
        
        // This is for netscape, it won't render the cell if it is empty.
        // Anyway, it doesn´t render the nice black separation line. 
        // This is why everybody uses resized images for this effect.
        // (these are the kind of arguments they used to beat MS...)        
        if ($value != 0) $text = "&nbsp;"; else $text = "";
        
        echo "<tr bgcolor='".$seriesColors[$bgColor]."'><td height=\"$currHeight\">$text</td></tr>\n";
        $bgColor++;
        $childs[$key] = 0;                
    }
    echo "</table>\n";    
    echo "</td>\n";
}

/* 
 *
 */
function drawStackedColumnsData($recordset, &$childs, $maxValue, 
    $clientHeight, &$XColumnData)
{
    
    global $ttst_mainColumn;
    global $ttst_childColumn;
    global $ttst_countColumn;

    $fields = ttdb_getArray($recordset);
    $oldMainColumnValue = $fields[$ttst_mainColumn];
    $XColumnData[] = $oldMainColumnValue;  // add to the x axis data array
    
    do {    
        
        $newMainColumnValue = $fields[$ttst_mainColumn];   
        
        if ($oldMainColumnValue != $newMainColumnValue) {          

            // mainColumn value changed, draw child table
            drawColumnData(&$childs, $maxValue, $clientHeight);
            $oldMainColumnValue = $newMainColumnValue;            
            $XColumnData[] = $oldMainColumnValue;  // add to the x axis data array
        }

        $childs[$fields[$ttst_childColumn]] = $fields[$ttst_countColumn];
    } while (($fields  = ttdb_getArray($recordset)) != false);
    
    // Last change is not detected, draw it manually.
    drawColumnData(&$childs, $maxValue, $clientHeight);   
    
}

/* 
 * Draws the legend of the given graph
 */
function drawLegendData($fields)
{
    global $seriesColors;
    
    echo "<table align='center' border='0' cellpadding='10' cellspacing='0'>\n";
    echo "<tr>\n";
    
    $colorIndex = 0;
    foreach ($fields as $key => $value) {
        echo "<td>\n";
        echo "<font size=2 color='".$seriesColors[$colorIndex++]."'>";
        echo "<b>".$key."</b>";
        echo "</font>\n</td>\n";        
    }
    
    echo "</tr>\n";
    echo "</table>\n";
}

/*
 *
 */
function drawXColumnData($XColumnData)
{
    global $ttst_mainColumn;    
    
    // It expects the trs from outside (maybe to specify height)
    echo "<td align='right'><font size=2>$ttst_mainColumn:</font></td>";
    foreach ($XColumnData as $key => $value) {
        echo "<td align='center'>\n";
        echo "<font size=2>";
        echo "<b>".$value."</b>";
        echo "</font>\n</td>\n";        
    }   
    
}

/* Performs the actual drawing of the series. 
 *
 */
function drawStackedColumns($connection, $childs, $maxValue, $maxColumns)
{
    
    global $ttst_childColumn;
    global $ttst_dataQuery;
    global $ttst_graphWidth;
    global $ttst_graphHeight;

    // Issue query
    $res = ttdb_execQuery($connection, $ttst_dataQuery);        
        
    // primary table (format)
    echo "<table name='mainData' width='$ttst_graphWidth' border='0'".
         " height='$ttst_graphHeight' align='center' cellspacing='0'".
         " cellpadding='0'>\n";
    
    // first row: data maximum
    echo "<tr height='".TEXT_ROW_HEIGHT."'>\n";
    echo "<td valign='bottom' align='right'><font size=2>$maxValue".
         "</font></td>\n";
    echo "</tr>\n";

    // separation line
    echo "<tr>\n";
    for ($i = 0; $i <= $maxColumns; $i++) {
        if ($i == 0) 
            $width = Y_SERIE_WIDTH;
        else
            $width = $ttst_graphWidth / $maxColumns;
        echo "<td bgcolor='black' height=1 width=".
        $width.">";
    }
    echo "</tr>\n";    

    // second row: cell with minValue (always 0) and columns
    echo "<tr>\n";
    echo "<td width='".Y_SERIE_WIDTH."' valign='bottom' align='right'>".
        "<font size=2>0</font></td>\n";  // First cell with legend    
    
    drawStackedColumnsData($res, $childs, $maxValue, 
        $ttst_graphHeight - 3 * TEXT_ROW_HEIGHT, $XColumnData);
    
    echo "</tr>\n";
    
    // separation line
    echo "<tr>\n";
    for ($i = 0; $i <= $maxColumns; $i++) echo "<td bgcolor='black' height=1>";
    echo "</tr>\n";    
    
    // mainColumn data row
    echo "<tr height='".TEXT_ROW_HEIGHT."'>\n";
    
    drawXColumnData($XColumnData);
    
    echo "</tr>\n";

    echo "</table>";  // primary table
    
    // legend (childColumn data)
    drawLegendData($childs);
    
}


/* Private renderer implementation depending on the type of graphic 
 * selected. 
 */
function ttst_renderStackedColumns($connection)
{
    global $ttst_numLevels;
    global $ttst_mainColumn;
    global $ttst_childColumn;
    global $ttst_dataQuery;
    global $ttst_graphicType;   // opengl jamás se programará en php...
    
    // first case, only one level
    if ($ttst_numLevels == 1) {
        
    } 

    // Second case, two levels have to be handled
    if ($ttst_numLevels == 2) {
        
        // Get child elements
        getChildElements($connection, $childs);
        
        // Get the limits for those elements
        getChildLimits($connection, $childs, $maxValue, $maxColumns);
        
        // Draw the table
        drawStackedColumns($connection, $childs, $maxValue, $maxColumns);
    } 

}


/* __ Exported API ________________________________________________________ */

/*
 *
 */
function ttst_render($connection)
{
    // Check that all needed parameters are available
    global $ttst_numLevels;
    global $ttst_mainColumn;
    global $ttst_childColumn;
    global $ttst_dataQuery;
    global $ttst_graphicType;   // opengl jamás se programará en php...
    global $ttst_countColumn;
    
    // First, data columns. 
    switch ($ttst_numLevels) {
    case 1: 
        if ($ttst_mainColumn == "") {
            tter_error("Main column not set, Renderer will not be invoked.");
            return;
        }
        break;
    case 2: 
        if ($ttst_mainColumn == "") {
            tter_error("Main column not set, Renderer will not be invoked.");
            return;
        }
        if ($ttst_childColumn == "") {
            tter_error("Child column not set, Renderer will not be invoked.");
            return;
        }
        break;
    default:
        tter_error("Number of graphic levels not correct ($ttst_numLevels). ".
                   "Renderer will not be invoked");
        return;
    }
    
    // Second, query
    if ($ttst_dataQuery == "") {    
        tter_error("Statistic query not set, Renderer will not be invoked.");
        return;
    }
    
    // Invoke the right renderer based on the graphic type. 
    switch ($ttst_graphicType) {
    case TTST_GRAPHTYPE_STACKED_COLUMNS: 
        return ttst_renderStackedColumns($connection);
    default: 
        tter_error("Specified graphic type not supported.");
    }
}


/* __ Required Functions __________________________________________________ */

/*  It will always be neeeded to call at least these funtions in order to
 * invoke the render method. 
 */

/*
 *
 */
function ttst_setDataQuery($query)
{
    // TODO: add validity checks
    global $ttst_dataQuery;
    
    $ttst_dataQuery = $query;
}

/*
 *
 */
function ttst_setMainColumn($columnName)
{
    // TODO: add validity checks
    global $ttst_mainColumn;
    $ttst_mainColumn = $columnName;
}

/*
 *
 */
function ttst_setChildColumn($columnName)
{
    // TODO: add validity checks
    global $ttst_childColumn;
    $ttst_childColumn = $columnName;
}

/*
 *
 */
function ttst_setCountColumn($columnName)
{
    // TODO: add validity checks
    global $ttst_countColumn;
    $ttst_countColumn = $columnName;
}

/*
 *
 */
function ttst_setNumberOfLevels($numLevels)
{
    // TODO: add validity checks
    global $ttst_numLevels;
    $ttst_numLevels = $numLevels;
}

/*
 *
 */
function ttst_setGraphicType($type)
{
    // TODO: add validity checks
    global $ttst_graphicType;
    $ttst_graphicType = $type;
}

/*
 *
 */
function ttst_setGraphicDimensions($width, $height)
{
    global $ttst_graphWidth, $ttst_graphHeight;
    $ttst_graphWidth = $width;
    $ttst_graphHeight = $height;
}

?>
Return current item: Druid Defect Control