Skip to content
Snippets Groups Projects
ajax.reports.php 9.41 KiB
Newer Older
<?php
/*********************************************************************
    ajax.reports.php

    AJAX interface for reports -- both plot and tabular data are retrievable
    in JSON format from this utility. Please put plumbing in /scp/ajax.php
    pattern rules.

    Jared Hancock <jared@osticket.com>
    Copyright (c)  2006-2013 osTicket
    http://www.osticket.com

    Released under the GNU General Public License WITHOUT ANY WARRANTY.
    See LICENSE.TXT for details.

    vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/

if(!defined('INCLUDE_DIR')) die('403');

include_once(INCLUDE_DIR.'class.ticket.php');
include_once(INCLUDE_DIR.'class.report.php');
 * The overview report allows for the display of basic ticket statistics in
 * both graphical and tabular formats.
 */
class OverviewReportAjaxAPI extends AjaxController {
    function enumTabularGroups() {
        return $this->encode(array("dept"=>__("Department"), "topic"=>__("Topics"),
            # XXX: This will be relative to permissions based on the
            # logged-in-staff. For basic staff, this will be 'My Stats'
            "staff"=>__("Agent")));
        list($start, $stop) = $this->_getDateRange();

        $groups = array(
            "dept" => array(
                "table" => DEPT_TABLE,
                "pk" => "id",
                "sort" => 'T1.name',
                "fields" => 'T1.name',
                "headers" => array(__('Department')),
                "filter" => ('T1.id IN ('.implode(',', db_input($thisstaff->getDepts())).')')
            ),
            "topic" => array(
                "table" => TOPIC_TABLE,
                "pk" => "topic_id",
                "sort" => 'name',
                "fields" => "CONCAT_WS(' / ',"
                    ."(SELECT P.topic FROM ".TOPIC_TABLE." P WHERE P.topic_id = T1.topic_pid),"
                    ."T1.topic) as name ",
                "headers" => array(__('Help Topic')),
                "filter" => '1'
            ),
            "staff" => array(
                "table" => STAFF_TABLE,
                "pk" => 'staff_id',
                "sort" => 'name',
                "fields" => "CONCAT_WS(' ', T1.firstname, T1.lastname) as name",
                "headers" => array(__('Agent')),
                    ('T1.staff_id=S1.staff_id
                      (T1.staff_id='.db_input($thisstaff->getId())
                        .(($depts=$thisstaff->getManagedDepartments())?
                            (' OR T1.dept_id IN('.implode(',', db_input($depts)).')'):'')
                        .($thisstaff->hasPerm(ReportModel::PERM_AGENTS)?
                            (' OR T1.dept_id IN('.implode(',', db_input($thisstaff->getDepts())).')'):'')
            )
        );
        $group = $this->get('group', 'dept');
        $info = isset($groups[$group])?$groups[$group]:$groups['dept'];

        # XXX: Die if $group not in $groups

            array(5, 'SELECT '.$info['fields'].',
                COUNT(*)-COUNT(NULLIF(A1.state, "created")) AS Opened,
                COUNT(*)-COUNT(NULLIF(A1.state, "assigned")) AS Assigned,
                COUNT(*)-COUNT(NULLIF(A1.state, "overdue")) AS Overdue,
                COUNT(*)-COUNT(NULLIF(A1.state, "closed")) AS Closed,
                COUNT(*)-COUNT(NULLIF(A1.state, "reopened")) AS Reopened
            FROM '.$info['table'].' T1
                LEFT JOIN '.TICKET_EVENT_TABLE.' A1
                    ON (A1.'.$info['pk'].'=T1.'.$info['pk'].'
                         AND NOT annulled
                         AND (A1.timestamp BETWEEN '.$start.' AND '.$stop.'))
                LEFT JOIN '.STAFF_TABLE.' S1 ON (S1.staff_id=A1.staff_id)
            WHERE '.$info['filter'].'
            GROUP BY T1.'.$info['pk'].'
            ORDER BY '.$info['sort']),

            array(1, 'SELECT '.$info['fields'].',
                FORMAT(AVG(DATEDIFF(T2.closed, T2.created)),1) AS ServiceTime
            FROM '.$info['table'].' T1
                LEFT JOIN '.TICKET_TABLE.' T2 ON (T2.'.$info['pk'].'=T1.'.$info['pk'].')
                LEFT JOIN '.STAFF_TABLE.' S1 ON (S1.staff_id=T2.staff_id)
            WHERE '.$info['filter'].' AND T2.closed BETWEEN '.$start.' AND '.$stop.'
            GROUP BY T1.'.$info['pk'].'
            ORDER BY '.$info['sort']),

            array(1, 'SELECT '.$info['fields'].',
                FORMAT(AVG(DATEDIFF(B2.created, B1.created)),1) AS ResponseTime
            FROM '.$info['table'].' T1
                LEFT JOIN '.TICKET_TABLE.' T2 ON (T2.'.$info['pk'].'=T1.'.$info['pk'].')
Peter Rotich's avatar
Peter Rotich committed
                LEFT JOIN '.THREAD_TABLE.' B0 ON (B0.object_id=T2.ticket_id
                    AND B0.object_type="T")
                LEFT JOIN '.THREAD_ENTRY_TABLE.' B2 ON (B2.thread_id = B0.id
                    AND B2.`type`="R")
                LEFT JOIN '.THREAD_ENTRY_TABLE.' B1 ON (B2.pid = B1.id)
                LEFT JOIN '.STAFF_TABLE.' S1 ON (S1.staff_id=B2.staff_id)
            WHERE '.$info['filter'].' AND B1.created BETWEEN '.$start.' AND '.$stop.'
            GROUP BY T1.'.$info['pk'].'
            ORDER BY '.$info['sort'])
        foreach ($queries as $q) {
            list($c, $sql) = $q;
            $res = db_query($sql);
            while ($row = db_fetch_row($res)) {
                $found = false;
                foreach ($rows as &$r) {
                    if ($r[0] == $row[0]) {
                        $r = array_merge($r, array_slice($row, -$c));
                        $found = true;
                        break;
                    }
                }
                if (!$found)
                    $rows[] = array_merge(array($row[0]), array_slice($row, -$c));
            # Make sure each row has the same number of items
            foreach ($rows as &$r)
                while (count($r) < $cols)
                    $r[] = null;
        }
        return array("columns" => array_merge($info['headers'],
                        array(__('Opened'),__('Assigned'),__('Overdue'),__('Closed'),__('Reopened'),
                              __('Service Time'),__('Response Time'))),
                     "data" => $rows);
    }

    function getTabularData() {
        return $this->encode($this->getData());
    }

    function downloadTabularData() {
        $data = $this->getData();
        $csv = '"' . implode('","',$data['columns']) . '"';
        foreach ($data['data'] as $row)
            $csv .= "\n" . '"' . implode('","', $row) . '"';
            sprintf('%s-report.csv', $this->get('group', __('Department'))),
            'text/csv', $csv);
    function _getDateRange() {
        global $cfg;
        if(($start = $this->get('start', 'last month'))) {
            $stop = $this->get('period', 'now');
        } else {
            $start = 'last month';
            $stop = $this->get('period', 'now');
       $start = strtotime($start);

        if (substr($stop, 0, 1) == '+')
            $stop = strftime('%Y-%m-%d ', $start) . $stop;

        $start = 'FROM_UNIXTIME('.$start.')';
        $stop = 'FROM_UNIXTIME('.strtotime($stop).')';

        return array($start, $stop);
    }

    function getPlotData() {
        list($start, $stop) = $this->_getDateRange();

        # Fetch all types of events over the timeframe
        $res = db_query('SELECT DISTINCT(state) FROM '.TICKET_EVENT_TABLE
            .' WHERE timestamp BETWEEN '.$start.' AND '.$stop
                .' ORDER BY 1');
        $events = array();
        while ($row = db_fetch_row($res)) $events[] = $row[0];

        # TODO: Handle user => db timezone offset
        # XXX: Implement annulled column from the %ticket_event table
        $res = db_query('SELECT state, DATE_FORMAT(timestamp, \'%Y-%m-%d\'), '
                .'COUNT(ticket_id)'
            .' FROM '.TICKET_EVENT_TABLE
            .' WHERE timestamp BETWEEN '.$start.' AND '.$stop
            .' AND NOT annulled'
            .' GROUP BY state, DATE_FORMAT(timestamp, \'%Y-%m-%d\')'
            .' ORDER BY 2, 1');
        # Initialize array of plot values
        $plots = array();
        foreach ($events as $e) { $plots[$e] = array(); }

        $time = null; $times = array();
        # Iterate over result set, adding zeros for missing ticket events
        $slots = array();
        while ($row = db_fetch_row($res)) {
            $row_time = strtotime($row[1]);
            if ($time != $row_time) {
                # New time (and not the first), figure out which events did
                # not have any tickets associated for this time slot
                if ($time !== null) {
                    # Not the first record -- add zeros all the arrays that
                    # did not have at least one entry for the timeframe
                    foreach (array_diff($events, $slots) as $slot)
                        $plots[$slot][] = 0;
                }
                $slots = array();
                $times[] = $time = $row_time;
            }
            # Keep track of states for this timeframe
            $slots[] = $row[0];
            $plots[$row[0]][] = (int)$row[2];
        }
        foreach (array_diff($events, $slots) as $slot)
            $plots[$slot][] = 0;
        return $this->encode(array("times" => $times, "plots" => $plots,
            "events"=>$events));