Created
October 10, 2018 21:07
-
-
Save halfak/ebbf452eb50b6664bb3eded07c3a60cb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
( function ( mw, $ ) { | |
/** | |
* @class OresApi | |
*/ | |
/** | |
* @property {Object} defaultOptions Default options for #ajax calls. Can be overridden by passing | |
* `options` to OresApi constructor. | |
* @property {Object} defaultOptions.parameters Default query parameters for API requests. | |
* @property {Object} defaultOptions.ajax Default options for jQuery#ajax. | |
* @private | |
*/ | |
var defaultOptions = { | |
parameters: { | |
format: 'json' | |
}, | |
ajax: { | |
timeout: 30 * 1000, // 30 seconds | |
dataType: 'json', | |
type: 'GET' | |
}, | |
score: { | |
batchSize: 50, | |
parallelConnections: 2 | |
} | |
}, | |
OresApi; | |
/** | |
* Constructor to create an object to interact with the API of an ORES server. | |
* OresApi objects represent the API of a particular ORES server. | |
* | |
* var ores = require('ext.ores.api'); | |
* ores.get( { | |
* revids: [1234, 1235], | |
* models: [ 'damaging', 'articlequality' ] // same effect as 'damaging|articlequality' | |
* } ).done( function ( data ) { | |
* console.log( data ); | |
* } ); | |
* | |
* @constructor | |
* @param {Object} [options] See #defaultOptions documentation above. | |
* @param {Object} config configuration of the ores service. | |
*/ | |
OresApi = function ( options, config ) { | |
var defaults = $.extend( {}, options ), | |
setsUrl = options && options.ajax && options.ajax.url !== undefined; | |
defaults.parameters = $.extend( {}, defaultOptions.parameters, defaults.parameters ); | |
defaults.ajax = $.extend( {}, defaultOptions.ajax, defaults.ajax ); | |
if ( setsUrl ) { | |
defaults.ajax.url = String( defaults.ajax.url ); | |
} else { | |
defaults.ajax.url = config.baseUrl + '/v3/scores/' + config.wikiId; | |
} | |
this.defaults = defaults; | |
this.requests = []; | |
}; | |
OresApi.prototype = { | |
/** | |
* Abort all unfinished requests issued by this Api object. | |
* | |
* @method | |
*/ | |
abort: function () { | |
this.requests.forEach( function ( request ) { | |
if ( request ) { | |
request.abort(); | |
} | |
} ); | |
}, | |
/** | |
* Massage parameters from the nice format we accept into a format suitable for the API. | |
* | |
* @private | |
* @param {Object} parameters (modified in-place) | |
*/ | |
preprocessParameters: function ( parameters ) { | |
var key; | |
for ( key in parameters ) { | |
if ( Array.isArray( parameters[ key ] ) ) { | |
parameters[ key ] = parameters[ key ].join( '|' ); | |
} else if ( parameters[ key ] === false || parameters[ key ] === undefined ) { | |
// Boolean values are only false when not given at all | |
delete parameters[ key ]; | |
} | |
} | |
}, | |
/** | |
* Perform an API call. | |
* | |
* @param {Object} parameters | |
* @return {jQuery.Promise} Done: API response data and the jqXHR object. | |
* Fail: Error code | |
*/ | |
get: function ( parameters ) { | |
var requestIndex, | |
api = this, | |
apiDeferred = $.Deferred(), | |
xhr, | |
ajaxOptions; | |
parameters = $.extend( {}, this.defaults.parameters, parameters ); | |
ajaxOptions = $.extend( {}, this.defaults.ajax ); | |
this.preprocessParameters( parameters ); | |
ajaxOptions.data = $.param( parameters ); | |
xhr = $.ajax( ajaxOptions ) | |
.done( function ( result, textStatus, jqXHR ) { | |
var code; | |
if ( result.error ) { | |
code = result.error.code === undefined ? 'unknown' : result.error.code; | |
apiDeferred.reject( code, result, jqXHR ); | |
} else { | |
apiDeferred.resolve( result, jqXHR ); | |
} | |
} ); | |
requestIndex = this.requests.length; | |
this.requests.push( xhr ); | |
xhr.always( function () { | |
api.requests[ requestIndex ] = null; | |
} ); | |
return apiDeferred.promise( { abort: xhr.abort } ).fail( function ( code, details ) { | |
if ( !( code === 'http' && details && details.textStatus === 'abort' ) ) { | |
mw.log( 'OresApi error: ', code, details ); | |
} | |
} ); | |
}, | |
/** | |
* Prepare an array of revision IDs or objects containing an "id" parameter | |
* for batching. Accepts either an array of integers or an array of obj. | |
* Returns an array of obj. | |
* | |
* @param {Array} ids | |
* @return {Array} | |
*/ | |
prepareIdDataResults: function( ids ){ | |
var idDataResults = []; | |
for ( i = 0; i < ids.length; i++ ) { | |
if ( isFinite(ids[i]) ) { | |
idDataResults.push({id: ids[i]}); | |
} else { | |
idDataResults.push(ids[i]); | |
} | |
} | |
return idDataResults; | |
}, | |
/** | |
* Score a batch of ids if there are any remaining batches to score. This | |
* function will recurse until there are no more batches to process. | |
* | |
* @param {Array} idBatches An array of objects containing "id" and maybe also "data" | |
* @param {Array} models An array of model names | |
* @param {jQuery.Deferred} deferred A Deferred object to notify() score results to | |
* @return null | |
*/ | |
scoreBatch: function( idBatches, models, deferred ) { | |
var idBatch, rawIds; | |
if(ifBatches.length > 0 ) { | |
idBatch = idBatches.shift(); | |
rawIds = []; | |
for ( i = 0; i < idBatch.length; i++){ | |
rawIds.push(idBatch[i].id); | |
} | |
this.get({revids: rawIds, models: models}) | |
.done(function(result){ | |
for ( i = 0; i < idBatch.length; i++ ) { | |
deferred.notify(result[config.wikiId].scores[idBatch[i].id], | |
idBatch[i].data); | |
} | |
}); | |
setTimeout(function(){ | |
this.scoreBatch(idBatches, models, deferred); | |
}.bind(this), 0) | |
} | |
}, | |
/** | |
* Score set of ids in batches and return a jQuery.Promise object that will | |
* repreatedly call a function passed to progress() with results. | |
* | |
* @param {Array} ids An array of integers or objects with an "id" and "data" tag. "Data" will be passed to progress() as results come in. | |
* @param {Array} models An array of model names | |
* @param {Object} options Options for processing. E.g. "batchSize" and "parallelConnections" | |
* @return null | |
*/ | |
score: function ( ids, models, options ) { | |
var idBatches = [], | |
ids = this.prepareIdDataResults(ids), | |
batchSize = option.batchSize || this.defaults.score.batchSize, | |
parallelConnections = options.parallelConnections || this.defaults.score.parallelConnections, | |
deferred = new $.Deferred(); | |
// Initialize batches | |
for ( var i = 0, j=ids.length; i < j; i += batchSize ) { | |
idBatch = ids.slice(i, i + batchSize); | |
idBatches.push(idBatch); | |
} | |
// Start workers | |
for ( var i = 0; i < parallelConnections; i += 1 ) { | |
setTimeout(function(){ | |
this.scoreBatch(idBatches, models, deferred, i); | |
}.bind(this), 0); | |
} | |
return deferred.promise(); | |
} | |
}; | |
module.exports = new OresApi( {}, mw.config.get( 'extOresApiConfig' ) ); | |
}( mediaWiki, jQuery ) ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment