Skip to content

Commit

Permalink
Merge pull request #511 from brandonsavage/nightly-crashes
Browse files Browse the repository at this point in the history
Bug 640238 - Nightly Crash Trends
  • Loading branch information
brandonsavage committed Apr 24, 2012
2 parents 6c89079 + e693515 commit c6121aa
Show file tree
Hide file tree
Showing 16 changed files with 1,471 additions and 4 deletions.
66 changes: 65 additions & 1 deletion docs/middleware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ New-style, documented services
* `/crashes/paireduuid <#crashes-paireduuid>`_
* `/crashes/signatures <#crashes-signatures>`_
* `extensions/ <#id7>`_
* `crashtrends/ <#crashtrends>`_
* `job/ <#job>`_
* `priorityjobs/ <#priorityjobs>`_
* `products/ <#products>`_
* `products/builds/ <#products-builds>`_
* products/
* `products/builds/ <#products-builds>`_
* `products/versions/ <#products-versions>`_
* report/
* `report/list/ <#list-report>`_
* `signatureurls <#signature-urls>`_
Expand Down Expand Up @@ -514,7 +518,67 @@ Return a list of extensions::


.. ############################################################################
Job API
Crash Trends API
############################################################################
Crash Trends
----------

Return a list of nightly or aurora crashes that took place between two dates.

API specifications
^^^^^^^^^^^^^^^^^^

+----------------+---------------------------------------------------------------------------------------------------------------+
| HTTP method | GET |
+----------------+---------------------------------------------------------------------------------------------------------------+
| URL schema | /crashtrends/(optional_parameters) |
+----------------+---------------------------------------------------------------------------------------------------------------+
| Full URL | /crashtrends/start_date/(start_date)/end_date/(end_date)/product/(product)/version/(version) |
+----------------+---------------------------------------------------------------------------------------------------------------+
| Example | http://socorro-api/bpapi/crashtrends/start_date/2012-03-01/end_date/2012-03-15/product/Firefox/version/13.0a1 |
+----------------+---------------------------------------------------------------------------------------------------------------+

Mandatory parameters
^^^^^^^^^^^^^^^^^^^^

+---------------+---------------+---------------+-----------------------------------+
| Name | Type of value | Default value | Description |
+===============+===============+===============+===================================+
| start_date | Datetime | None | The earliest date of crashes |
| | | | we wish to evaluate |
+---------------+---------------+---------------+-----------------------------------+
| end_date | Datetime | None | The latest date of crashes we |
| | | | wish to evaluate. |
+---------------+---------------+---------------+-----------------------------------+
| product | String | None | The product. |
+---------------+---------------+---------------+-----------------------------------+
| version | String | None | The version. |
+---------------+---------------+---------------+-----------------------------------+
Optional parameters
^^^^^^^^^^^^^^^^^^^

None

Return value
^^^^^^^^^^^^

Return a total of crashes, along with their build date, by build ID::

[
{
"build_date": "2012-02-10",
"version_string": "12.0a2",
"product_version_id": 856,
"days_out": 6,
"report_count": 515,
"report_date": "2012-02-16",
"product_name": "Firefox"
}
]

.. ############################################################################
Products Builds API
############################################################################
Job
Expand Down
2 changes: 2 additions & 0 deletions scripts/config/webapiconfig.py.dist
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ import socorro.middleware.versions_info_service as versions_info
import socorro.middleware.products_builds_service as products_builds
import socorro.middleware.signature_summary_service as signature_summary
import socorro.middleware.signature_urls_service as signature_urls
import socorro.middleware.crash_trends_service as crash_trends
import socorro.middleware.tcbs_service as tcbs
import socorro.middleware.crashes_comments_service as crashes_comments
import socorro.middleware.crash_service as crash_new
Expand Down Expand Up @@ -161,6 +162,7 @@ servicesList.default = [
products_builds.ProductsBuilds,
signature_summary.SignatureSummary,
signature_urls.SignatureURLs,
crash_trends.CrashTrends,
tcbs.TCBS,
crashes_comments.CrashesComments,
crash_new.Crash,
Expand Down
67 changes: 67 additions & 0 deletions socorro/external/postgresql/crash_trends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import logging
import psycopg2

from socorro.external.postgresql.base import PostgreSQLBase
import socorro.database.database as db
from socorro.lib import datetimeutil, external_common

logger = logging.getLogger("webapi")


class CrashTrends(PostgreSQLBase):

def get(self, **kwargs):
filters = [
("start_date", None, "datetime"),
("end_date", None, "datetime"),
("product", None, "str"),
("version", None, "str"),
]

params = external_common.parse_arguments(filters, kwargs)
results = [] # So we have something to return.

query_string = """SELECT product_name,
version_string,
product_version_id,
report_date,
nightly_builds.build_date,
days_out,
sum(report_count) as report_count
FROM nightly_builds
JOIN product_versions USING ( product_version_id )
WHERE report_date <= %(end_date)s
AND report_date >= %(start_date)s
AND product_name = %(product)s
AND version_string = %(version)s
GROUP BY product_name,
version_string,
product_version_id,
report_date,
nightly_builds.build_date,
days_out"""

try:
connection = self.database.connection()
cursor = connection.cursor()
sql_results = db.execute(cursor, query_string, params)
except psycopg2.Error:
logger.error("Failed retrieving crashtrends data from PostgreSQL",
exc_info=True)
else:
for trend in sql_results:
row = dict(zip((
"product_name",
"version_string",
"product_version_id",
"report_date",
"build_date",
"days_out",
"report_count"), trend))
row['report_date'] = datetimeutil.date_to_string(row['report_date'])
row['build_date'] = datetimeutil.date_to_string(row['build_date'])
results.append(row)
finally:
connection.close()
results = {'crashtrends' : results}
return results
20 changes: 20 additions & 0 deletions socorro/middleware/crash_trends_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import logging

from socorro.middleware.service import DataAPIService

logger = logging.getLogger("webapi")

class CrashTrends(DataAPIService):

service_name = "crash_trends"
uri = "/crashtrends/(.*)"

def __init__(self, config):
super(CrashTrends, self).__init__(config)
logger.debug('Crash trends service __init__')

def get(self, *args):
params = self.parse_query_string(args[0])
module = self.get_module(params)
impl = module.CrashTrends(config=self.context)
return impl.get(**params)
9 changes: 9 additions & 0 deletions webapp-php/application/config/routes.php-dist
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,12 @@ $config['/signature_summary'] =

$config['/signature_summary/json_data'] =
'signature_summary/json_data';

$config['/crash_trends'] =
'crash_trends/index';

$config['/crash_trends/json_data'] =
'crash_trends/json_data';

$config['/crash_trends/product_versions'] =
'crash_trends/product_versions';
142 changes: 142 additions & 0 deletions webapp-php/application/controllers/crash_trends.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php defined('SYSPATH') or die('No direct script access.');

require_once(Kohana::find_file('libraries', 'timeutil', TRUE, 'php'));

class Crash_Trends_Controller extends Controller {

public function __construct() {
parent::__construct();
$this->crash_trends_model = new Crash_Trends_Model();
$this->branch_model = new Branch_Model();
}

/**
* Public functions map to routes on the controller
* http://<base-url>/NewReport/index/[product, version, ?'foo'='bar', etc]
*/
public function index() {
$d = array('product' => 'Firefox', 'version' => '', 'start_date' => '', 'end_date' => '');
$params = $this->getRequestParameters($d);

$params = $this->solveForDates($params);
$params = $this->solveForVersion($params);

$urlParams = array('product' => $params['product'],
'version' => $params['version'],
'start_date' => $params['start_date'],
'end_date' => $params['end_date']);
$this->setViewData(array(
'product' => $params['product'],
'version' => $params['version'],
'start_date' => $params['start_date'],
'end_date' => $params['end_date'],
'data_url' => url::site('crash_trends/json_data') . '?' . html::query_string($urlParams),

));
}

public function json_data() {
$d = array('product' => 'Firefox', 'version' => '', 'start_date' => '', 'end_date' => '');
$params = $this->getRequestParameters($d);

$params = $this->solveForDates($params);
$params = $this->solveForVersion($params);

$values = $this->crash_trends_model->getCrashTrends($params['product'],
$params['version'],
$params['start_date'],
$params['end_date']);

$values = $values->crashtrends;

if(empty($values)) {
echo json_encode(array());
exit;
}

$formatted = array();
$initial_arr = array('0' => 0, '1' => 0, '2' => 0, '3' => 0, '4' => 0, '5' => 0, '6' => 0, '7' => 0, '8' => 0);
foreach($values as $value)
{
if(!isset($formatted[$value->report_date])) {
// Initialize a particular build date's data array
$formatted[$value->report_date] = $initial_arr;
}

if($value->days_out >= 8) {
$formatted[$value->report_date]['8'] += $value->report_count;
} else {
$formatted[$value->report_date][$value->days_out] += $value->report_count;
}
}
ksort($formatted);

#echo json_encode($formatted);

$graph_format = array();

foreach($initial_arr as $k => $v) {
$increment = 0;
$data_array = array();
foreach($formatted as $data_point) {
$data_array[] = array($data_point[$k], $increment);
$increment += '1.5';
}

// Make it so that the report displays properly.
if($k == 8) {
$k = '8+';
}

$graph_format[] = array('label' => $k . ' Days', 'data' => $data_array);
}


$graph_format = array('nightlyCrashes' => $graph_format);

echo json_encode($graph_format);
exit;
}

/** Returns JSON **/
public function product_versions() {
$d = array('product' => 'Firefox');
$params = $this->getRequestParameters($d);
$result = $this->branch_model->getProductVersionsByProduct($params['product']);
$return = array();

foreach($result as $version) {
if($version->release == 'Nightly' ||
$version->release == 'Aurora') {
$return[] = $version->version;
}
}

echo json_encode($return);
exit;
}

protected function solveForDates(array $params) {
$dt = new DateTime('today');
if(empty($params['end_date'])) {
$params['end_date'] = $dt->format('Y-m-d');
}

if(empty($params['start_date'])) {
$dt->modify('- 7 days');
$params['start_date'] = $dt->format('Y-m-d');
}

return $params;
}

protected function solveForVersion(array $params) {
if(empty($params['version'])) {
$p = $this->branch_model->getRecentProductVersion($params['product'], 'Nightly');
$params['version'] = $p->version;
}

return $params;
}
}
?>
5 changes: 2 additions & 3 deletions webapp-php/application/models/branch.php
Original file line number Diff line number Diff line change
Expand Up @@ -467,13 +467,12 @@ public function getByProductVersion($product, $version) {
* @param string product
* @return object Branch data
*/
public function getRecentProductVersion($product) {
public function getRecentProductVersion($product, $channel = 'Release') {
$resp = $this->_getValues();
$date = time();

foreach($resp as $item) {
if( $item->product == $product AND
$item->release == 'Release' AND
$item->release == $channel AND
strtotime($item->start_date) <= $date AND
strtotime($item->end_date) >= $date) {
return $item; // Essentially a LIMIT 1, per old query
Expand Down
26 changes: 26 additions & 0 deletions webapp-php/application/models/crash_trends.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
class Crash_Trends_Model extends Model {

public function getCrashTrends($product, $version, $start_date, $end_date)
{
$config = array();
$credentials = Kohana::config('webserviceclient.basic_auth');
if($credentials) {
$config['basic_auth'] = $credentials;
}
$service = new Web_Service($config);
$host = Kohana::config('webserviceclient.socorro_hostname');
$product = rawurlencode($product);
$version = rawurlencode($version);
$start_date = rawurlencode($start_date);
$end_date = rawurlencode($end_date);

$url = "{$host}/crashtrends/start_date/{$start_date}/end_date/{$end_date}/product/{$product}/version/{$version}";

$resp = $service->get($url);
return $resp;
}


}
?>
Loading

0 comments on commit c6121aa

Please sign in to comment.