Quantcast
Channel: Uncategorized – DO-RE-M(IT)
Viewing all articles
Browse latest Browse all 14

Tickets per requester group per month heatmap for (GLPI) ticket analysis

$
0
0

Recently we where losing allot of time on bogus work. With the huge number of different sources we are facing and dealing with aprox 6~8K tickets each month it can be hard to figure out where you can make big steps quickly in improving the situation.

Because GLPI is opensource it was very easy to query the database. After identifying the right queries it required only a small amount a extra time to build a realtime dashboard with drilldown options. (PHP code shared below).

2019-07-24 15_57_14-Window

I made each of the months downdrillable outputting the creationdate, customername, and ticket title in a quick overview. This way you can quickly scan the type of tickets created and analyze them quickly per customer.

2019-07-24 16_02_59-Mozilla Firefox

Total cost: 4 hours coding, a allready running AMP environment (GLPI).

Want the same overviews using your own (power)BI application? Here are the queries used by my report.

PHP snippet:

<?php

print('
  <html>
  <head>

  <style>
     body{ font-family:calibri; }
     #main a:link, a:visited{
       padding: 4px 2px 0px 2px;
       display:block;
       width:75px;
       height:20px;
       color:#3468eb;
       text-decoration:none;
       font-weight:bold;
     }

    #main a:hover{
      padding: 4px 2px 0px 2px;
      display:block;
      width:75px;
      height:20px;
      color:white;
      text-decoration:none;
       font-weight:bold;
    }

    #main td{
      padding:1px 2px 0px 2px;
      width:77px;
      text-align:left;
      line-height:25px;
    }

    #sec {
      padding: 1px 1px 1px 1px;
      margin:0;
    }

#sec td{
padding:1px 2px 0px 2px;
text-align:left;
line-height:25px;
background-color:#EEE;
}

#sec th{
text-align:left;
background-color:#DDD;
}

#sec a:link, :visited {
display:block;
width:185px;
padding:0px 0px 0px 10px;
text-decoration:none;
color:#3468eb;
}

#sec a:hover {
display:block;
width:185px;
padding: 0px 0px 0px 10px;
background-color:#029c66;
color:#FFF;
}

</style>

</head>
<body>
');

##############################################
// EDIT VALUE BELOW TO MATCH YOUR REQUIREMENTS
// MaxValue for red color
$max=750;

###############################################
// Use your own database connector here
// for instance mysqli
// EDIT RULE BELOW TO MATCH YOUR ENVIRONMENT
$db = new db();


##############################################
// Catch uri input. Might need some more filtering
// This script is only used internally so im skipping
// this part now.
$year = (isset($_GET['y'])) ? date('Y',mktime(0,0,0,1,1,$_GET['y'])) : date("Y");
$cdetail = (isset($_GET['cd'])) ? $_GET['cd'] : false;


#############################################
// If we are not looking a a specific customer (GLPI Requester Group)
// then we show the overview with all requestergroups.
if(!$cdetail){

// Perform the query for each of the months in the selected year.
// or of the current year if no year was selected.
for ( $i = 1; $i < 13; $i++)
{

// Create a start date (first day of a given $i month)
// Create a end date (last day of a given $i month)
$sdate = $year."-$i-1";
$edate = $year."-$i-".date("t");

// This is the query we are performing for each month of the year
//
$sql = "SELECT distinct count(a.id) as aantal,
d.name as gname
FROM glpi_tickets as a
LEFT JOIN glpi_itilcategories as b ON a.itilcategories_id = b.id
INNER JOIN glpi_groups_tickets as c ON a.id = c.tickets_id
INNER JOIN glpi_groups as d ON c.groups_id = d.id
WHERE 1=1
AND a.is_deleted = 0
AND a.entities_id = 0
AND (date BETWEEN '$sdate 00:00:00' AND '$edate 23:59:59')
GROUP BY gname";


// Perform the query on the database object method query
// This method might need editing given your own database definition
// in the previous section.
if($r1 = $db->query($sql)){
// Loop through the resultset and build an array we can use later
while($row = $r1->fetch_array(MYSQLI_ASSOC)){
$results['TOTAAL Tickets OPS'][$i] += $row['aantal'];
$results[$row['gname']][$i] = $row['aantal'];
}
}
}

// perform a var_dump($results) here if you want to debug the query and resultset;

// Show a simple html table with nice headers
echo "<table id='main'>\n\t<tr>\n\t\t<th width='170' style='text-align:right;'>Klantnaam</th>";
for($i=1; $i < 13; $i++){ echo "\t\t<th>".date("M",mktime(0,0,0,$i,1,1990))."</th>\n"; }
echo "\t</tr>\n";

// Parse the resultset of the previous query to readable HTML
foreach($results as $customer => $data){
for($i = 0; $i < 13; $i++){
if($i == 0 ){
echo "\t<tr>\n";
echo "\t\t<td style='text-align:right;'>$customer</td>\n";
}else{
#calculeer trend tov vorige maand
if($i == 1) { $last = $data[$i]; }
$trend = ($last <= $data[$i]) ? "<font>▲ &nbsp;&nbsp; </font>" : "<font color='#FFF'>▼ &nbsp; &nbsp;</font>";

if(is_numeric($data[$i])){

#background
$back = make_color($data[$i], 1, $max);

echo "\t\t<td style='background-color:$back;'><a href='{$_SERVER['PHP_SELF']}?cd={$customer}::{$i}&y=$year' target='_self'>$trend {$data[$i]}</a></td>\n";
$last = $data[$i];
}else{
$back = '#EEE';
echo "\t\t<td style='background-color:$back'>&nbsp;</td>\n";
}
}
}
echo "\t</tr>\n";
}
echo "</table>\n";

// Done, here the overview page is ready..
// The alternative is we want to see some month/customer details.

}else{

// This is a failsave. if we find something weird of lacking just redirect back
// to the previous page and let em try again with a valid input.
if(!strstr($cdetail, '::') || strstr($cdetail, 'TOTAAL')){
header('location:'.$_SERVER['PHP_SELF']);
}else{
// split the customer and month from the URI
$detail = explode('::', $cdetail);

// Create a nice table with a 'back' button we can press so
// we can browse back to the overview.
echo "<table id='sec' style=''><tr><td id='button' width='150'><a href='{$_SERVER['PHP_SELF']}?y=$year'>Terug naar overzicht</a></td>";

$sdate = $year."-{$detail['1']}-1";
$edate = $year."-{$detail['1']}-".date("t");

echo "<td width='400'> Tickets tussen: $sdate 00:00:00 en $edate 23:59:59</td>";

// This is the query to find the tickets.
$sql = "SELECT a.id,
a.date,
a.name as tname,
d.name as gname
FROM glpi_tickets as a
LEFT JOIN glpi_itilcategories as b ON a.itilcategories_id = b.id
INNER JOIN glpi_groups_tickets as c ON a.id = c.tickets_id
INNER JOIN glpi_groups as d ON c.groups_id = d.id
WHERE 1=1
AND a.is_deleted = 0
AND a.entities_id = 0
AND (date BETWEEN '$sdate 00:00:00' AND '$edate 23:59:59')
AND d.name = '{$detail['0']}'
ORDER BY date";

// Perform the query
// This method might require editing reflecting your own database
// object / driver.
if($r1 = $db->query($sql)){

// Aantal is dutch for amount...
$aantal = mysqli_num_rows($r1);
// Resultaten is dutch for results, edit as you see fit :)
echo "<td width='200'>Aantal resultaten: $aantal</td></tr></table>";

// Create a simple table with nice headers
echo "<table id='sec'>
<tr>
<th width='190'>ticket id</th>
<th width='150'>date</th>
<th width='200'>customer name</th>
<th width='800'>ticket title<th>
</tr>";

// Loop through the resultset
// Edit the GLPI link to match your own environment, it makes the tickets clickable
// so you can view them in full detail within GLPI.
while($row = $r1->fetch_array(MYSQLI_ASSOC)){
echo "
<tr>
<td><a href='https://glpi.amis.nl/front/ticket.form.php?id={$row['id']}' target='_blank'>Open [AMIS #0{$row['id']}]</a></td>
<td>{$row['date']}</td>
<td>{$row['gname']}</td>
<td>{$row['tname']}</td>
</tr>";

}
}


}
}


#Make heatmap
# source: https://stackoverflow.com/questions/20423641/php-function-to-convert-hsl-to-rgb-or-hex
function ColorHSLToRGB($h, $s, $l){
$r = $l;
$g = $l;
$b = $l;
$v = ($l <= 0.5) ? ($l * (1.0 + $s)) : ($l + $s - $l * $s);
if ($v > 0){
$m;
$sv;
$sextant;
$fract;
$vsf;
$mid1;
$mid2;

$m = $l + $l - $v;
$sv = ($v - $m ) / $v;
$h *= 6.0;
$sextant = floor($h);
$fract = $h - $sextant;
$vsf = $v * $sv * $fract;
$mid1 = $m + $vsf;
$mid2 = $v - $vsf;

switch ($sextant)
{
case 0:
$r = $v;
$g = $mid1;
$b = $m;
break;
case 1:
$r = $mid2;
$g = $v;
$b = $m;
break;
case 2:
$r = $m;
$g = $v;
$b = $mid1;
break;
case 3:
$r = $m;
$g = $mid2;
$b = $v;
break;
case 4:
$r = $mid1;
$g = $m;
$b = $v;
break;
case 5:
$r = $v;
$g = $m;
$b = $mid2;
break;
}
}
return array('r' => $r * 255.0, 'g' => $g * 255.0, 'b' => $b * 255.0);
}

function make_color($value, $min = 0, $max = .5)
{
$ratio = $value;
if ($min > 0 || $max < 1) {
if ($value < $min) {
$ratio = 1;
} else if ($value > $max) {
$ratio = 0;
} else {
$range = $min - $max;
$ratio = ($value - $max) / $range;
}
}

$hue = ($ratio * 1.2) / 3.60;
$rgb = ColorHSLToRGB($hue, 1, .5);

$r = round($rgb['r'], 0);
$g = round($rgb['g'], 0);
$b = round($rgb['b'], 0);

#return "rgb($r,$g,$b)";
return sprintf("#%02x%02x%02x", $r, $g, $b);
}

print('</body></html>');

// Done, dont use the PHP close tag ;-)

 

Assumptions:

In our environment each ticket has a customer requester group assigned.

You should code in your own database object that supports $db->query($sql) or rewrite the queries to match your environment. See: https://www.php.net/manual/en/mysqli.query.php for more information.


Viewing all articles
Browse latest Browse all 14

Latest Images

Trending Articles





Latest Images