crash-report-tools/get-crashbugsweekly.php

445 строки
18 KiB
PHP
Executable File

#!/usr/bin/php
<?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// This script retrieves stats on weekly crash bugs.
// *** non-commandline handling ***
if (php_sapi_name() != 'cli') {
// not commandline, assume apache and output own source
header('Content-Type: text/plain; charset=utf8');
print(file_get_contents($_SERVER['SCRIPT_FILENAME']));
exit;
}
include_once('datautils.php');
// *** script settings ***
// turn on error reporting in the script output
ini_set('display_errors', 1);
// make sure new files are set to -rw-r--r-- permissions
umask(022);
// set default time zone - right now, always the one the server is in!
date_default_timezone_set('America/Los_Angeles');
// *** data gathering variables ***
// Start date to calculate stats for - always goes up until current week
$startdate = time() - 14.5 * 86400; //strtotime('2011-01-01');
$fake_counts = false; //true; // DEBUG only!
// *** URLs ***
$on_moz_server = file_exists('/mnt/crashanalysis/rkaiser/');
$outdir = 'crash-bugs';
if ($on_moz_server) { chdir('/mnt/crashanalysis/rkaiser/'); }
else { chdir('/mnt/mozilla/projects/socorro/'); }
// *** code start ***
// get current day
$curtime = time();
// make sure our output dir exists
if (!file_exists($outdir)) { mkdir($outdir); }
$bdfile = $outdir.'/crash.bugdata.json';
$fweb = '%s.weeklybugs.html';
if (file_exists($bdfile)) {
print('Reading stored crash bug data'."\n");
$bugdata = json_decode(file_get_contents($bdfile), true);
}
else {
$bugdata = array();
}
$calcweeks = array();
if ($startdate < strtotime('1998-01-01') || $startdate > $curtime) {
// if the argument results in no useful date, calculate this and last two weeks
$startdate = $curtime - 14.5 * 86400;
}
// first get start time of given week
$btime = getdate($startdate);
// week start day (0=sun,1=mon,...,6=sat; default to 1)
$wsd = 1;
// start time: difference of week day number and week start day before today
$dayoffset = -($btime['wday'] - $wsd); if ($dayoffset > 0) { $dayoffset -= 7; }
$starttime = strtotime($btime['year'].'-'.$btime['mon'].'-'.$btime['mday'].' '.$dayoffset.' day');
while ($starttime < ($curtime - 12*3600)) {
// start time of following week
$dayoffset += 7;
$nw_starttime = strtotime($btime['year'].'-'.$btime['mon'].'-'.$btime['mday'].' '.$dayoffset.' day');
$calcweeks[] = array('start'=>$starttime, 'end'=>$nw_starttime);
// set startttime for next possible week
$starttime = $nw_starttime;
}
$queryinfo = array('new' => array('title' => 'New',
'desc' => 'New bugs filed in the given week',
'color' => 'rgba(255, 127, 0, .5)'),
'fixed' => array('title' => 'Fixed',
'desc' => 'Bugs marked as FIXED in the given week',
'color' => 'rgba(0, 204, 0, .5)'),
'triaged' => array('title' => 'Triaged',
'desc' => 'Bugs resolved with a different resolution than FIXED in the given week (i.e. DUPLICATE, WORKSFORME, INVALID, etc.)',
'color' => 'rgba(0, 0, 255, .5)'),
'open' => array('title' => 'Open',
'desc' => 'Bugs open at the end of the given week (approximation as the query cannot account for reopened bugs)',
'color' => ''),
);
$bugqueries = array_keys($queryinfo);
// get stats for requested weeks
foreach ($calcweeks as $wkdef) {
$weekstart = date('Y-m-d', $wkdef['start']);
print('Fetching crash bug data for week of '.$weekstart."\n");
$weekdata = array();
foreach ($bugqueries as $querytype) {
$bugzilla_url = getPeriodBugURL($querytype, $wkdef['start'], $wkdef['end']);
$bugcount = getBugCount($bugzilla_url);
if ($bugcount !== false) {
$weekdata['count_'.$querytype] = $bugcount;
}
}
if (count($weekdata)) {
$weekdata['time_update'] = time();
$bugdata[$weekstart] = $weekdata;
}
else {
print('ERROR: No data!'."\n");
}
ksort($bugdata); // sort by date (key), ascending
file_put_contents($bdfile, json_encode($bugdata));
}
if (count($bugdata)) {
$firstyear = date('o', strtotime(min(array_keys($bugdata))));
$lastyear = date('o', strtotime(max(array_keys($bugdata))));
print('Data found for '.$firstyear.' through '.$lastyear.', writing output...'."\n");
for ($year = $firstyear; $year <= $lastyear; $year++) {
$yearfweb = $outdir.'/'.sprintf($fweb, $year);
// create out an HTML page
print('Writing HTML output for '.$year."\n");
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true; // we want a nice output
$cgraph = array('scale_x'=>10, 'height'=>300,
'offset_x'=>2, 'offset_y'=>2,
'rows'=>array(array('tblcolumn'=>2, 'scale'=>200, 'stack'=>false, 'fill'=>true,
'color'=>$queryinfo['new']['color']),
array('tblcolumn'=>3, 'scale'=>200, 'stack'=>false, 'fill'=>true,
'color'=>$queryinfo['fixed']['color']),
array('tblcolumn'=>4, 'scale'=>200, 'stack'=>true, 'fill'=>true,
'color'=>$queryinfo['triaged']['color'])));
$root = $doc->appendChild($doc->createElement('html'));
$head = $root->appendChild($doc->createElement('head'));
$title = $head->appendChild($doc->createElement('title',
'Weekly Crash Bugs - '.$year));
$body = $root->appendChild($doc->createElement('body'));
$h1 = $body->appendChild($doc->createElement('h1',
'Weekly Crash Bugs - '.$year));
// navigation
$pgnav = $doc->createDocumentFragment();
$pnav = $pgnav->appendChild($doc->createElement('p', 'Years'.': '));
$pnav->setAttribute('class', 'pages');
if ($year > $firstyear) {
// first page
$link = $pnav->appendChild($doc->createElement('a', '|&lt;'));
$link->setAttribute('href', sprintf($fweb, $firstyear));
$link->setAttribute('class', 'pagefirstlast');
// previous page
if ($year > $firstyear + 1) {
$pnav->appendChild($doc->createTextNode(' '));
$link = $pnav->appendChild($doc->createElement('a', '&lt;&lt;'));
$link->setAttribute('href', sprintf($fweb, $year - 1));
$link->setAttribute('class', 'pagebkfwd');
}
}
for ($i = $firstyear; $i <= $lastyear; $i++) {
$pnav->appendChild($doc->createTextNode(' '));
if ($i == $year) {
$pnum = $pnav->appendChild($doc->createElement('span', '['));
$pnum->setAttribute('class', 'pagenum curpage');
}
else {
$pnum = $pnav->appendChild($doc->createElement('a'));
$pnum->setAttribute('href', sprintf($fweb, $i));
$pnum->setAttribute('class', 'pagenum');
}
$pnum->appendChild($doc->createTextNode($i));
if ($i == $year) {
$pnum->appendChild($doc->createTextNode(']'));
}
}
if ($year < $lastyear) {
// next page
if ($year < $lastyear - 1) {
$pnav->appendChild($doc->createTextNode(' '));
$link = $pnav->appendChild($doc->createElement('a', '&gt;&gt;'));
$link->setAttribute('href', sprintf($fweb, $year+1));
}
// last page
$pnav->appendChild($doc->createTextNode(' '));
$link = $pnav->appendChild($doc->createElement('a', '&gt;|'));
$link->setAttribute('href', sprintf($fweb, $lastyear));
$link->setAttribute('class', 'pagefirstlast');
}
$body->appendChild($pgnav->cloneNode(true));
if (count($cgraph)) {
$canvas = $body->appendChild($doc->createElement('canvas'));
$canvas->setAttribute('id', 'graphcanvas');
}
// extract weeks belonging to this year
$yeardata = array();
foreach ($bugdata as $weekstart=>$weekdata) {
if (date('o', strtotime($weekstart)) == $year) {
$yeardata[$weekstart] = $weekdata;
}
}
if (count($yeardata)) {
$body->appendChild($doc->createElement('p',
sprintf('Weekly statistics on crash keyword Bugzilla activity in %s:', $year)));
$table = $body->appendChild($doc->createElement('table'));
$table->setAttribute('id', 'weeklybugs');
$table->setAttribute('class', 'border');
$thead = $table->appendChild($doc->createElement('thead'));
$trow = $thead->appendChild($doc->createElement('tr'));
$trow->appendChild($doc->createElement('th', 'Week'));
$trow->appendChild($doc->createElement('th', 'Start'));
$trow->appendChild($doc->createElement('th', 'End'));
foreach ($queryinfo as $querytype=>$qinfo) {
$th = $trow->appendChild($doc->createElement('th', $qinfo['title']));
if (strlen($qinfo['color'])) {
$th->setAttribute('style', 'background-color: '.$qinfo['color'].';');
}
}
$tbody = $table->appendChild($doc->createElement('tbody'));
foreach ($yeardata as $weekstart=>$weekdata) {
$week_start = strtotime($weekstart);
$week_end = strtotime(date('Y-m-d', $week_start).' +7 day');
$trow = $tbody->appendChild($doc->createElement('tr'));
$td = $trow->appendChild($doc->createElement('th', date('W/o', $week_start)));
$td->setAttribute('title', intval(date('W', $week_start)));
$td->setAttribute('class', 'week');
$td = $trow->appendChild($doc->createElement('td', date('Y-m-d', $week_start)));
$td->setAttribute('title', date('Y-m-d', $week_start));
$td->setAttribute('class', 'startdate small');
$td = $trow->appendChild($doc->createElement('td', date('Y-m-d', $week_end - 1)));
$td->setAttribute('title', date('Y-m-d', $week_end - 1));
$td->setAttribute('class', 'enddate small dis');
foreach ($bugqueries as $querytype) {
$td = $trow->appendChild($doc->createElement('td'));
$td->setAttribute('class', 'num');
$link = $td->appendChild($doc->createElement('a',
is_null($weekdata['count_'.$querytype])?'No data':$weekdata['count_'.$querytype]));
$link->setAttribute('href', getPeriodBugURL($querytype, $week_start, $week_end));
}
}
}
else {
$body->appendChild($doc->createElement('p', 'No bug statistics found.'));
}
if (count($cgraph)) {
$weeksperyear = date('W', strtotime($year.'-12-28')); // 28th of December is always in the last week
$script = $head->appendChild($doc->createElement('script'));
$script->setAttribute('type', 'text/javascript');
$graph =
'function canvasDraw() {'."\n"
.' var canvas = document.getElementById("graphcanvas");'."\n"
.' if (document.getElementById("weeklybugs")) { canvas.style.display=""; }'."\n"
.' else { canvas.style.display="none"; return; }'."\n"
.' var context = canvas.getContext("2d");'."\n"
.' canvas.width = '.($weeksperyear*$cgraph['scale_x']+4).';'."\n"
.' canvas.height = '.($cgraph['height']+4).';'."\n"
.' var gx = '.$cgraph['offset_x'].'; var gy = '.$cgraph['offset_y'].';'."\n"
.' var gwd = '.$weeksperyear.'; var ght = '.$cgraph['height'].';'."\n"
.' context.setTransform(1, 0, 0, -1, 0, canvas.height);'."\n"
.' context.fillStyle = "rgba(214, 204, 189, 1.0)";'."\n"
.' context.fillRect(0, 0, canvas.width, canvas.height);'."\n"
.' context.fillStyle = "rgba(236, 240, 248, 1.0)";'."\n"
.' context.fillRect(gx, gy, gwd*'.$cgraph['scale_x'].', ght);'."\n"
.' var tbody = document.getElementById("weeklybugs").getElementsByTagName("tbody")[0];'."\n"
.' var trows = tbody.getElementsByTagName("tr");'."\n"
.' var linewid = '.$cgraph['scale_x'].';'."\n"
.' var gridcolor = "rgba(127, 127, 127, .25)";'."\n";
foreach ($cgraph['rows'] as $idx=>$row) {
$graph .=
' var gval'.$idx.';'."\n"
.' var gcolor'.$idx.' = "'.$row['color'].'";'."\n"
.' var gscale'.$idx.' = '.$row['scale'].'/ght;'."\n";
}
$graph .=
' var dt_yearstart = new Date('.$year.', 0, 1);'."\n"
.' var dt_current, dtparts, ix;'."\n"
.' var lasttop = 0;'."\n"
.' for (var i = 0; i < tbody.children.length; i++) {'."\n"
.' wk_current = parseInt(trows[i].getElementsByTagName("th")[0].getAttribute("title")) - 1;'."\n"
.' ix = gx + wk_current * '.$cgraph['scale_x'].';'."\n"
.' sdtparts = trows[i].getElementsByTagName("td")[0].getAttribute("title").split("-");'."\n"
.' edtparts = trows[i].getElementsByTagName("td")[1].getAttribute("title").split("-");'."\n"
.' if ((edtparts[1] > sdtparts[1]) && (edtparts[1] % 3 == 1)) {'."\n"
.' context.fillStyle = gridcolor;'."\n"
.' context.fillRect(ix, gy, 1, ght);'."\n"
.' }'."\n";
foreach ($cgraph['rows'] as $idx=>$row) {
if (!$row['stack']) { $graph .= ' lasttop = 0;'."\n"; }
$graph .=
' gval'.$idx.' = parseInt'
.'(trows[i].getElementsByTagName("td")['.$row['tblcolumn'].'].getElementsByTagName("a")[0].firstChild.nodeValue);'."\n"
.' context.fillStyle = gcolor'.$idx.';'."\n"
.' context.fillRect(ix, gy + '.($row['fill']?'lasttop':'gval'.$idx.'/gscale'.$idx.' - 1')
.', linewid, '.($row['fill']?'gval'.$idx.'/gscale'.$idx:'1').');'."\n"
.' lasttop = gval'.$idx.'/gscale'.$idx.';'."\n";
}
$graph .=
' }'."\n"
.'}'."\n";
$script->appendChild($doc->createCDATASection($graph));
$scriptelem = $body->appendChild($doc->createElement('script', 'canvasDraw();'));
$scriptelem->setAttribute('type', 'text/javascript');
}
$style = $head->appendChild($doc->createElement('style'));
$style->setAttribute('type', 'text/css');
$style->appendChild($doc->createCDATASection(
'body {'."\n"
.' background: #FFFFFF;'."\n"
.' color: #000000;'."\n"
.' font-family: sans-serif;'."\n"
.'}'."\n"
.'.small {'."\n"
.' font-size: small;'."\n"
.'}'."\n"
.'.dis { color: #808080; }'."\n"
.'th { font-size: small; }'."\n"
.'table.border {'."\n"
.' border-spacing: 0px;'."\n"
.' border-collapse: collapse;'."\n"
.' empty-cells: show;'."\n"
.' border-left: 1px solid #404040;'."\n"
.' border-top: 1px solid #404040;'."\n"
.'}'."\n"
.'table.border th, table.border td {'."\n"
.' border-bottom: 1px solid #404040;'."\n"
.' border-right: 1px solid #404040;'."\n"
.'}'."\n"
.'table.border td {'."\n"
.' padding-left: 3px;'."\n"
.' padding-right: 3px;'."\n"
.'}'."\n"
.'td.num {'."\n"
.' text-align: right;'."\n"
.'}'."\n"
.'p.pages .pagenum.curpage, p.pages #curpage {'."\n"
.' font-weight: bold; color: #000000;'."\n"
.'}'."\n"
));
$body->appendChild($pgnav);
$body->appendChild($doc->createElement('p', 'Legend:'));
$table = $body->appendChild($doc->createElement('table'));
$table->setAttribute('class', 'border');
$tbody = $table->appendChild($doc->createElement('tbody'));
foreach ($queryinfo as $querytype=>$qinfo) {
$trow = $tbody->appendChild($doc->createElement('tr'));
$th = $trow->appendChild($doc->createElement('th', $qinfo['title']));
if (strlen($qinfo['color'])) {
$th->setAttribute('style', 'background-color: '.$qinfo['color'].';');
}
$td = $trow->appendChild($doc->createElement('td', $qinfo['desc']));
}
$doc->saveHTMLFile($yearfweb);
}
print("\n");
}
// debug only line
// print_r($flashdata);
// *** helper functions ***
function getPeriodBugURL($scheme, $date_start, $date_end = null) {
$ymd_start = date('Y-m-d', $date_start);
if (is_null($date_end)) {
$date_end = strtotime($ymd_start.' +7 day');
}
$ymd_end = date('Y-m-d', $date_end);
$bugzilla_url = 'https://bugzilla.mozilla.org/buglist.cgi?keywords=crash';
switch ($scheme) {
case 'new':
// new bugs
$bugzilla_url .= '&chfieldfrom='.$ymd_start;
$bugzilla_url .= '&chfieldto='.$ymd_end.'&chfield=%5BBug%20creation%5D';
break;
case 'fixed':
// fixed bugs
$bugzilla_url .= '&resolution=FIXED';
$bugzilla_url .= '&chfieldfrom='.$ymd_start;
$bugzilla_url .= '&chfieldto='.$ymd_end.'&chfield=resolution';
break;
case 'triaged':
// triaged (resolved non-fixed) bugs
$bugzilla_url .= '&resolution=INVALID&resolution=WONTFIX&resolution=DUPLICATE';
$bugzilla_url .= '&resolution=WORKSFORME&resolution=INCOMPLETE&resolution=EXPIRED';
$bugzilla_url .= '&resolution=MOVED';
$bugzilla_url .= '&chfieldfrom='.$ymd_start;
$bugzilla_url .= '&chfieldto='.$ymd_end.'&chfield=resolution';
break;
case 'open':
// open bugs at the end of the period (ignores bugs that get reopened)
$bugzilla_url .= '&field0-0-0=creation_ts&type0-0-0=lessthan&value0-0-0='.$ymd_end;
$bugzilla_url .= '&negate1=1';
$bugzilla_url .= '&field1-0-0=bug_status&type1-0-0=changedto&value1-0-0=RESOLVED';
$bugzilla_url .= '&field1-1-0=bug_status&type1-1-0=changedbefore&value1-1-0='.$ymd_end;
break;
case 'default':
$bugzilla_url = '';
break;
}
return $bugzilla_url;
}
function getBugCount($listurl) {
if (preg_match('/buglist\.cgi(\?.*)$/', $listurl, $regs)) {
$list_json = file_get_contents('https://api-dev.bugzilla.mozilla.org/latest/count'.$regs[1]);
if ($list_json) {
$list_info = json_decode($list_json, true);
return $list_info['data'];
}
}
return false;
}
?>