Fdans has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/347305 )
Change subject: [wip] Unifies usage of aqs api and datasets api ...................................................................... [wip] Unifies usage of aqs api and datasets api Bug: T161933 Change-Id: I10f37f9998b83d07f7c411b62563de2521e7a90b --- M src/app/apis/aqs-api.js M src/components/visualizers/visualizer/visualizer.js 2 files changed, 116 insertions(+), 104 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/analytics/dashiki refs/changes/05/347305/1 diff --git a/src/app/apis/aqs-api.js b/src/app/apis/aqs-api.js index 8f339d2..e9379b5 100644 --- a/src/app/apis/aqs-api.js +++ b/src/app/apis/aqs-api.js @@ -27,91 +27,118 @@ }); } - /** - * Parameters - * metric : An object representing a metric. - * project : A Wiki project database name (enwiki, commonswiki, etc). - * accessMethods : Access methods to split data by. - * Returns - * A promise that wraps data for the metric/project transformed via the converter - * Mobile, desktop and app breakdowns translate to different requests. - */ - AQSApi.prototype.getData = function (metric, project, accessMethods) { - var deferred = new $.Deferred(), - apiConfig = this.config.aqsApi[metric.name], - converter = this.dataConverters[metric.name]; + AQSApi.prototype = { + /** + * Parameters + * metric : An object representing a metric. + * project : A Wiki project database name (enwiki, commonswiki, etc). + * accessMethods : Access methods to split data by. + * Returns + * A promise that wraps data for the metric/project transformed via the converter + * Mobile, desktop and app breakdowns translate to different requests. + */ + getData: function (metric, projects, accessMethods) { + var converter = this.dataConverters[metric.name]; + var deferred = new $.Deferred(), + apiConfig = this.config.aqsApi[metric.name]; - if (!apiConfig) { - deferred.resolve(new TimeseriesData()); + if (!apiConfig) { + deferred.resolve(new TimeseriesData()); + return deferred.promise(); + } + + // Normalize the access method breakdown. + if (!accessMethods || !accessMethods.length) { + accessMethods = ['All']; + } + accessMethods = _.map(accessMethods, function (method) { + return apiConfig.breakdownOptions[method]; + }); + + if (typeof projects === 'string') { + projects = [projects]; + } + + var promises = []; + apiConfig.projectMap = {}; + + projects.forEach(function (project) { + // Get the project URL with sitematrix. + var sitematrixPromise = sitematrix.getProjectUrl(this.config, project); + function handleProjectUrl(projectUrl) { + var granularity = metric.granularity || 'daily'; + // Perform the requests. + apiConfig.projectMap[projectUrl.replace('.org', '')] = project; + promises = promises.concat(getPromisesForAccessMethods( + accessMethods, + projectUrl, + granularity, + apiConfig + )); + if(promises.length === projects.length * accessMethods.length) { + handlePromisesFinished(promises, apiConfig, converter, deferred); + } + } + sitematrixPromise.done(handleProjectUrl); + sitematrixPromise.fail(function () { + handleProjectUrl('all-projects'); + }); + }.bind(this)); return deferred.promise(); } - - // Normalize the access method breakdown. - if (!accessMethods || !accessMethods.length) { - accessMethods = ['All']; - } - accessMethods = _.map(accessMethods, function (method) { - return apiConfig.breakdownOptions[method]; - }); - - // Get the project URL with sitematrix. - var sitematrixPromise = sitematrix.getProjectUrl(this.config, project); - sitematrixPromise.done(function (projectUrl) { - var promises = [], - granularity = metric.granularity || 'daily', - endDate = moment().format(apiConfig.dateFormat[granularity]), - breakdownCamelCase = _.camelCase(apiConfig.breakdownParameter); - - // Perform the requests. - _.forEach(accessMethods, function (method) { - var params = { - project: projectUrl, - agent: 'user', // Only used in the Pageviews endpoint. - granularity: granularity, - start: apiConfig.dataStart, - end: endDate - }; - params[breakdownCamelCase] = method; - promises.push(pageviews[apiConfig.endpoint](params)); - }); - - // Return when we have all data. - Promise.all(promises).then(function (data) { - var timeseries = [], - breakdownKebabCase = _.kebabCase(apiConfig.breakdownParameter); - - _.forEach(data, function (dataForMethod) { - // Use the dbname as the label, to match colors with Project Selector - // if we want to re-label, we need to change both. - var opt = { - label: project, - pattern: dataForMethod.items[0][breakdownKebabCase] - }; - timeseries.push(converter(opt, dataForMethod)); - }); - - // Datasets are disjoint but we need to add them so that - // the timeseries object makes sense. - var ts = TimeseriesData.mergeAll(timeseries) - deferred.resolve(ts); - - }, function (reason) { - deferred.resolve(new TimeseriesData()); - logger.error(reason); - }).catch(function (error) { - deferred.resolve(new TimeseriesData()); - logger.error(error); - }); - - }); - - sitematrixPromise.fail(function () { - //something went wrong, make sure to return empty data - deferred.resolve(new TimeseriesData()); - }); - - return deferred.promise(); }; + function getPromisesForAccessMethods (methods, projectUrl, granularity, apiConfig) { + var breakdownCamelCase = _.camelCase(apiConfig.breakdownParameter), + endDate = moment().format(apiConfig.dateFormat[granularity]); + return methods.map(function (method) { + var params = { + project: projectUrl, + agent: 'user', // Only used in the Pageviews endpoint. + granularity: granularity, + start: apiConfig.dataStart, + end: endDate + }; + params[breakdownCamelCase] = method; + return pageviews[apiConfig.endpoint](params); + }); + } + + function handlePromisesFinished (promises, apiConfig, converter, deferred) { + // Return when we have all data. + Promise.all(promises).then(function (data) { + deferred.resolve(_generateTimeseriesForAllData( + data, + apiConfig, + converter + )); + }, function (reason) { + deferred.resolve(new TimeseriesData()); + logger.error(reason); + }).catch(function (error) { + deferred.resolve(new TimeseriesData()); + logger.error(error); + }); + } + + function _generateTimeseriesForAllData (data, apiConfig, converter) { + var timeseries = [], + breakdownKebabCase = _.kebabCase(apiConfig.breakdownParameter); + + _.forEach(data, function (dataForMethod) { + // Use the dbname as the label, to match colors with Project Selector + // if we want to re-label, we need to change both. + var opt = { + label: apiConfig.projectMap[dataForMethod.items[0].project], + pattern: dataForMethod.items[0][breakdownKebabCase] + }; + timeseries.push(converter(opt, dataForMethod)); + }.bind(this)); + + // Datasets are disjoint but we need to add them so that + // the timeseries object makes sense. + return TimeseriesData.mergeAll(timeseries); + } + return new AQSApi(siteConfig); }); diff --git a/src/components/visualizers/visualizer/visualizer.js b/src/components/visualizers/visualizer/visualizer.js index 19002a9..18cfe41 100644 --- a/src/components/visualizers/visualizer/visualizer.js +++ b/src/components/visualizers/visualizer/visualizer.js @@ -39,30 +39,15 @@ } this.data = ko.observable(new TimeseriesData()); - - // don't love this , the point of having an api finder is to have a common interface - // for apis such it doesn't matter which one we are using - if (apiType === 'aqsApi'){ - var aqs = params.aqs; - var projects = aqs.projects; - - // no changes should be needed to api if we wait until all promises are resolved to render - var promises = projects.map(function (project) { - return api.getData({'name':aqs.name, 'granularity':aqs.granularity}, project); - }); - - //invoqued when all promises are done - Promise.all(promises).then(function (data) { - this.data(TimeseriesData.mergeAll(data)); - }.bind(this)); - - - } else { - api.getData(graph, 'all').done(this.data); + var projects = 'all'; + var aqs = params.aqs; + if (aqs) { + graph.granularity = aqs.granularity; + graph.name = aqs.name; + projects = aqs.projects; } - - - + var metricInfo = graph; + api.getData(metricInfo, projects).done(this.data); this.startDate = ko.observable(graph.startDate); this.minDate = ko.observable(graph.startDate); this.endDate = ko.observable(); -- To view, visit https://gerrit.wikimedia.org/r/347305 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I10f37f9998b83d07f7c411b62563de2521e7a90b Gerrit-PatchSet: 1 Gerrit-Project: analytics/dashiki Gerrit-Branch: master Gerrit-Owner: Fdans <fd...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits