Skip to content

Commit

Permalink
Big refactoring.
Browse files Browse the repository at this point in the history
* Better code
* Caching
* Uses job queue to pull from bugzilla
  • Loading branch information
LegNeato committed Aug 9, 2011
1 parent 274ea29 commit b62c6d9
Show file tree
Hide file tree
Showing 8 changed files with 450 additions and 138 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
*.swp
150 changes: 27 additions & 123 deletions Bugzilla.class.php
@@ -1,148 +1,52 @@
<?php <?php


require_once 'HTTP/Request2.php'; require_once(dirname(__FILE__) . '/BugzillaOutput.class.php');
require_once('smarty/Smarty.class.php');
require_once(dirname(__FILE__) . '/Utils.php');


// Factory
class Bugzilla { class Bugzilla {


// Set variables and initialize the backend public static function create($config=array(), $opts=array(), $title='') {
function __construct($url, $config=array(), $id=FALSE) {
$this->url = $url;
$this->id = $id;
$this->error = FALSE;
$this->data = FALSE;

$this->_configure($config);
}

private function _configure( $config ) {

// Default configuration // Default configuration
// TODO: This should be in the main configuration // FIXME: This should be in the main configuration
$this->config = array( $theconfig = array(
'type' => 'bug', 'type' => 'bug',
'display' => 'table', 'display' => 'table',
); );


// Overlay user's desired configuration // Overlay user's desired configuration
foreach( $config as $key => $value ) { foreach( $config as $key => $value ) {
$this->config[$key] = $value; $theconfig[$key] = $value;
}
}

// Connect and get the data
public function fetch( $query_opts_raw = FALSE ) {

// Don't do anything if we already had an error
if( $this->error ) { return; }

// TODO: To support loading from the DB in the future
if( $this->id ) {
$this->_fetch_by_id();
return;
}

// Make sure query options are valid JSON
$opts = json_decode($query_opts_raw);
if( !$query_opts_raw || !$opts ) {
$this->error = 'Query options must be valid json';
return;
}

// Do the actual fetching
$this->_fetch_by_options($opts);
}

private function _fetch_by_id() {
// TODO: Stub
}

// Load data from the Bugzilla REST API
private function _fetch_by_options($opts) {

// Set up our HTTP request
$request = new HTTP_Request2($this->url . "/" . $this->config['type'],
HTTP_Request2::METHOD_GET,
array('follow_redirects' => TRUE,
// TODO: Not sure if I should do this
'ssl_verify_peer' => FALSE));

// The REST API requires these
$request->setHeader('Accept', 'application/json');
$request->setHeader('Content-Type', 'application/json');

// Add in the requested query options
$url = $request->getUrl();
$url->setQueryVariables(get_object_vars($opts));

// This is basically straight from the HTTP/Request2 docs
try {
$response = $request->send();
if (200 == $response->getStatus()) {
$this->data = json_decode($response->getBody());
} else {
$this->error = 'Server returned unexpected HTTP status: ' .
$response->getStatus() . ' ' .
$response->getReasonPhrase();
return;
}
} catch (HTTP_Request2_Exception $e) {
$this->error = $e->getMessage();
return;
} }


// Now that we have the data, process it // Generate the proper object
$this->_process_data(); switch( $theconfig['display'] ) {

case 'list':
} $b = new BugzillaList($theconfig, $opts, $title);

break;
private function _process_data() {
// TODO: Stub
}

public function render() {


global $wgBugzillaSmartyTemplateDir; case 'bar':
global $wgBugzillaSmartyCompileDir; $b = new BugzillaBarGraph($theconfig, $opts, $title);
global $wgBugzillaSmartyConfigDir; break;
global $wgBugzillaSmartyCacheDir;

// If we have an error, render it out instead
if( $this->error ) {
return $this->_render_error();
}


// No error, we're good to go case 'vbar':
$smarty = new Smarty(); $b = new BugzillaVerticalBarGraph($theconfig, $opts, $title);
$smarty->template_dir = $wgBugzillaSmartyTemplateDir; break;
$smarty->compile_dir = $wgBugzillaSmartyCompileDir;
$smarty->config_dir = $wgBugzillaSmartyConfigDir;
$smarty->cache_dir = $wgBugzillaSmartyCacheDir;


case 'pie':
$b = new BugzillaPieGraph($theconfig, $opts, $title);
break;


// TODO: This is basically a prototype, needs to be better case 'inline':
if( $this->config['display'] == 'table' ) { $b = new BugzillaInline($theconfig, $opts, $title);
$smarty->assign('bugs', $this->data->bugs); break;
return $smarty->fetch('bug/table.tpl');


elseif( $this->config['display'] == 'bar' ) { case 'table':
$smarty->assign('type', 'bhs'); default:
#$smarty->assign('type', 'p'); $b = new BugzillaTable($theconfig, $opts, $title);
$smarty->assign('size', '200x300');
$smarty->assign('x_labels', implode('|', $this->data->x_labels));
$smarty->assign('data', implode(',', $this->data->data));
return $smarty->fetch('count/bar.tpl');
} }


} return $b;


private function _render_error() {
$what = (!empty($this->error)) ? $this->error : 'Unknown Error';
return "<div class='bugzilla error'>Bugzilla Error: $what</div>";
} }

} }


?> ?>
64 changes: 50 additions & 14 deletions Bugzilla.php
@@ -1,6 +1,5 @@
<?php <?php



// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Extension credits / metadata // Extension credits / metadata
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
Expand All @@ -19,19 +18,59 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


// Register the classes to autoload // Register the classes to autoload
$wgAutoloadClasses['Bugzilla'] = dirname(__FILE__) . '/Bugzilla.class.php'; $wgAutoloadClasses['Bugzilla'] = dirname(__FILE__) .
'/Bugzilla.class.php';
$wgAutoloadClasses['BugzillaQuery'] = dirname(__FILE__) .
'/BugzillaQuery.class.php';
$wgAutoloadClasses['BugzillaOutput'] = dirname(__FILE__) .
'/BugzillaOutput.class.php';
$wgAutoloadClasses['BugzillaJob'] = dirname(__FILE__) .
'/BugzillaJob.class.php';

// -----------------------------------------------------------------------------
// Register our background job
// -----------------------------------------------------------------------------

$wgJobClasses['queryBugzillaUpdate'] = 'BugzillaUpdateJob';
$wgJobClasses['queryBugzillaInsert'] = 'BugzillaInsertJob';



// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Register for MediaWiki hooks // Register for MediaWiki hooks
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


$wgHooks['BeforePageDisplay'][] = 'BugzillaIncludeHTML'; $wgHooks['LoadExtensionSchemaUpdates'][] = 'BugzillaCreateCache';
$wgHooks['ParserFirstCallInit'][] = 'BugzillaParserInit'; $wgHooks['BeforePageDisplay'][] = 'BugzillaIncludeHTML';
$wgHooks['ParserFirstCallInit'][] = 'BugzillaParserInit';


// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Hook work functions // Hook work functions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


// Schema updates for the database cache
function BugzillaCreateCache( $updater ) {
if( $updater === null ) {
// <= 1.16 support
global $wgExtNewTables;
global $wgExtModifiedFields;
$wgExtNewTables[] = array(
'bugzilla_cache',
dirname( __FILE__ ) . '/cache.sql'
);
}else {
// >= 1.17 support
$updater->addExtensionUpdate( array( 'addTable',
'bugzilla_cache',
dirname( __FILE__ ) . '/cache.sql',
TRUE )
);
}

// Let the other hooks keep processing
return TRUE;
}

// Add content to page HTML
function BugzillaIncludeHTML( &$out, &$sk ) { function BugzillaIncludeHTML( &$out, &$sk ) {


global $wgScriptPath; global $wgScriptPath;
Expand All @@ -58,12 +97,11 @@ function BugzillaIncludeHTML( &$out, &$sk ) {
// Add the script to do table magic // Add the script to do table magic
$out->addInlineScript('$(document).ready(function() { $out->addInlineScript('$(document).ready(function() {
$(".bugzilla").dataTable({ $(".bugzilla").dataTable({
"bJQueryUI": true, "bJQueryUI": true
"sPaginationType": "full_numbers"
})});'); })});');


// Let the other hooks keep processing // Let the other hooks keep processing
return true; return TRUE;
} }


// Hook our callback function into the parser // Hook our callback function into the parser
Expand All @@ -74,7 +112,7 @@ function BugzillaParserInit( Parser &$parser ) {
$parser->setHook( $wgBugzillaTagName, 'BugzillaRender' ); $parser->setHook( $wgBugzillaTagName, 'BugzillaRender' );


// Let the other hooks keep processing // Let the other hooks keep processing
return true; return TRUE;
} }


// Function to be called when our tag is found by the parser // Function to be called when our tag is found by the parser
Expand All @@ -86,12 +124,9 @@ function BugzillaRender($input, array $args, Parser $parser ) {
$parser->disableCache(); $parser->disableCache();


// Create a new bugzilla object // Create a new bugzilla object
$bz = new Bugzilla($wgBugzillaRESTURL, $args); $bz = Bugzilla::create($args, $input, $parser->getTitle());

// Talk to bugzilla
$bz->fetch($input);


// Show the results (or an error if there was one) // Show the desired output (or an error if there was one)
return $bz->render(); return $bz->render();
} }


Expand All @@ -103,6 +138,7 @@ function BugzillaRender($input, array $args, Parser $parser ) {
$wgBugzillaRESTURL = 'https://api-dev.bugzilla.mozilla.org/latest'; $wgBugzillaRESTURL = 'https://api-dev.bugzilla.mozilla.org/latest';
$wgBugzillaURL = 'https://bugzilla.mozilla.org'; $wgBugzillaURL = 'https://bugzilla.mozilla.org';
$wgBugzillaTagName = 'bugzilla'; $wgBugzillaTagName = 'bugzilla';
$wgBugzillaMethod = 'REST'; // XML-RPC and JSON-RPC may be supported later
$wgBugzillaUseCache = TRUE; $wgBugzillaUseCache = TRUE;
$wgBugzillaCacheMins = 5; $wgBugzillaCacheMins = 5;
$wgBugzillaJqueryTable = FALSE; $wgBugzillaJqueryTable = FALSE;
Expand All @@ -114,4 +150,4 @@ function BugzillaRender($input, array $args, Parser $parser ) {
$wgBugzillaSmartyConfigDir = dirname(__FILE__) . '/configs/'; $wgBugzillaSmartyConfigDir = dirname(__FILE__) . '/configs/';
$wgBugzillaSmartyCacheDir = '/tmp/'; $wgBugzillaSmartyCacheDir = '/tmp/';


?>
74 changes: 74 additions & 0 deletions BugzillaJob.class.php
@@ -0,0 +1,74 @@
<?php

abstract class BugzillaJob extends Job {

// Run the job
public function run() {

$this->query = unserialize($this->params['query_obj']);
$article = new Article( $this->title );

if( $article ) {

// Pull from Bugzilla
$this->query->_fetch_by_options();

// Mess with the database
$this->_database_work();

}

return TRUE;
}

}

class BugzillaInsertJob extends BugzillaJob {
// Set up the background job
public function __construct( $title, $params ) {
parent::__construct('queryBugzillaInsert', $title, $params );
}

public function _database_work() {

// Get the master because we are writing
$dbw = wfGetDB( DB_MASTER );

// Add it to the cache
$res = $dbw->insert(
'bugzilla_cache',
array('id' => $this->query->id(),
'fetched_at' => wfTimestamp(TS_DB),
'data' => serialize($this->query->data)),
__METHOD__
);

}

}

class BugzillaUpdateJob extends BugzillaJob {
// Set up the background job
public function __construct( $title, $params ) {
parent::__construct('queryBugzillaUpdate', $title, $params );
}

public function _database_work() {

// Get the master because we are writing
$dbw = wfGetDB( DB_MASTER );

// Update cache entry
$res = $dbw->update(
'bugzilla_cache',
array('id' => $this->query->id(),
'fetched_at' => wfTimestamp(TS_DB),
'data' => serialize($this->query->data)),
array('id' => $this->query->id()),
__METHOD__
);

}
}

?>

0 comments on commit b62c6d9

Please sign in to comment.