jenkins-bot has submitted this change and it was merged. Change subject: Stats: Separate total and language graph, and show draft count in graph ......................................................................
Stats: Separate total and language graph, and show draft count in graph This is in preparation for showing deletion stats and weekly trend graphs. Showing both total and language specific stats is difficult to understand because the difference is widening fast. So separated them. ApiQueryContentTranslationLanguageTrend now return cumulative count for translations in progress as well. Soon it should retun cumulative deleted stats too. Bug: T105192 Bug: T90538 Change-Id: Ifd4bd0e3a92ac022428edf3915552ea99142dbfb --- M api/ApiQueryContentTranslationLanguageTrend.php M includes/Translation.php M modules/stats/ext.cx.stats.js M modules/stats/styles/ext.cx.stats.less 4 files changed, 230 insertions(+), 73 deletions(-) Approvals: Nikerabbit: Looks good to me, approved jenkins-bot: Verified diff --git a/api/ApiQueryContentTranslationLanguageTrend.php b/api/ApiQueryContentTranslationLanguageTrend.php index cd5bf33..8bbf549 100644 --- a/api/ApiQueryContentTranslationLanguageTrend.php +++ b/api/ApiQueryContentTranslationLanguageTrend.php @@ -12,6 +12,8 @@ * * @ingroup API ContentTranslationAPI */ +use ContentTranslation\Translation; + class ApiQueryContentTranslationLanguageTrend extends ApiQueryBase { public function __construct( $query, $moduleName ) { @@ -32,7 +34,11 @@ $result->addValue( array( 'query' ), 'contenttranslationlangtrend', - ContentTranslation\Translation::getTrend( $source, $target, $interval ) + array( + 'translations' => + Translation::getPublishTrend( $source, $target, $interval ), + 'drafts' => Translation::getDraftTrend( $source, $target, $interval ) + ) ); } diff --git a/includes/Translation.php b/includes/Translation.php index c0a7c98..0f54508 100644 --- a/includes/Translation.php +++ b/includes/Translation.php @@ -211,10 +211,94 @@ } /** + * Get time-wise cumulative number of drafts for given + * language pairs, with given interval. + * @param string $source Source language code + * @param string $target Target language code + * @param string $interval 'weekly' or 'monthly' trend + * @return array + */ + public static function getDraftTrend( $source, $target, $interval ) { + $dbr = Database::getConnection( DB_SLAVE ); + + $draftCondition = $dbr->makeList( + array( + 'translation_status' => 'draft', + 'translation_target_url IS NULL' + ), + LIST_AND + ); + + $conditions = array(); + $conditions[] = $draftCondition; + if ( $source !== null ) { + $conditions['translation_source_language'] = $source; + } + if ( $target !== null ) { + $conditions['translation_target_language'] = $target; + } + + $options = null; + if ( $interval === 'week' ) { + $options = array( + 'GROUP BY' => array( + 'YEARWEEK(translation_last_updated_timestamp)', + ), + ); + } elseif ( $interval === 'month' ) { + $options = array( + 'GROUP BY' => array( + 'YEAR(translation_last_updated_timestamp), MONTH(translation_last_updated_timestamp)', + ), + ); + } + + $subQuery = $dbr->selectSQLText( + 'cx_translations', + 'count(*)', + $dbr->makeList( array( + 'translation_last_updated_timestamp <= MAX(translations.translation_last_updated_timestamp)', + $dbr->makeList( $conditions, LIST_AND ), + ), + LIST_AND ) + ); + + $rows = $dbr->select( + array( 'translations' => 'cx_translations' ), + array( + "translations.translation_last_updated_timestamp AS date", + '(' . $subQuery . ') translatons_count', + ), + $dbr->makeList( $conditions, LIST_AND ), + __METHOD__, + $options + ); + + $prev = 0; + $result = array(); + foreach ( $rows as $row ) { + $count = (int)$row->translatons_count; + $result[] = array( + 'date' => $interval === 'week' ? + // Week end date + date( 'Y-m-d', strtotime( $row->date . ' + ' . + ( 6 - date( 'w', strtotime( $row->date ) ) ) . ' days' ) ) : + date( 'Y-m', strtotime( $row->date ) ), + 'count' => $count, + 'delta' => $count - $prev, + ); + + $prev = $count; + } + + return $result; + } + + /** * Get time-wise cumulative number of translations for given * language pairs, with given interval. */ - public static function getTrend( $source, $target, $interval ) { + public static function getPublishTrend( $source, $target, $interval ) { $dbr = Database::getConnection( DB_SLAVE ); $publishedCondition = $dbr->makeList( @@ -224,50 +308,48 @@ ), LIST_OR ); + $conditions = array(); + $conditions[] = $publishedCondition; + if ( $source !== null ) { + $conditions['translation_source_language'] = $source; + } + if ( $target !== null ) { + $conditions['translation_target_language'] = $target; + } - $groupBy = null; - + $options = null; if ( $interval === 'week' ) { - $groupBy = array( + $options = array( 'GROUP BY' => array( 'YEARWEEK(translation_last_updated_timestamp)', ), ); } elseif ( $interval === 'month' ) { - $groupBy = array( + $options = array( 'GROUP BY' => array( 'YEAR(translation_last_updated_timestamp), MONTH(translation_last_updated_timestamp)', ), ); } - $conditions = array( $publishedCondition ); - - if ( $source !== null ) { - $conditions['translation_source_language'] = $source; - } - - if ( $target !== null ) { - $conditions['translation_target_language'] = $target; - } - + $subQuery = $dbr->selectSQLText( + 'cx_translations', + 'count(*)', + $dbr->makeList( array( + 'translation_last_updated_timestamp <= MAX(translations.translation_last_updated_timestamp)', + $dbr->makeList( $conditions, LIST_AND ), + ), + LIST_AND ) + ); $rows = $dbr->select( array( 'translations' => 'cx_translations' ), array( - "DATE_FORMAT( translations.translation_last_updated_timestamp, '%Y-%m-%d' ) AS date", - '(' . $dbr->selectSQLText( - 'cx_translations', - 'count(*)', - $dbr->makeList( array( - 'translation_last_updated_timestamp <= MAX(translations.translation_last_updated_timestamp)', - $dbr->makeList( $conditions, LIST_AND ), - ), - LIST_AND ) - ) . ') translatons_count', + "translations.translation_last_updated_timestamp AS date", + '(' . $subQuery . ') translatons_count', ), $dbr->makeList( $conditions, LIST_AND ), __METHOD__, - $groupBy + $options ); $prev = 0; @@ -323,7 +405,7 @@ * @param string $to Target language code * @param int $limit Number of records to fetch atmost * @param in $offset Offset from which at most $limit records to fetch - * @return array[] + * @return array */ public static function getAllPublishedTranslations( $from, $to, $limit, $offset ) { $dbr = Database::getConnection( DB_SLAVE ); diff --git a/modules/stats/ext.cx.stats.js b/modules/stats/ext.cx.stats.js index 15bd6fc..b0a0245 100644 --- a/modules/stats/ext.cx.stats.js +++ b/modules/stats/ext.cx.stats.js @@ -32,8 +32,14 @@ $( '<div>' ).addClass( 'bounce3' ) ); this.$highlights = $( '<div>' ).addClass( 'cx-stats-highlights' ); - this.$graph = $( '<canvas>' ).attr( { - id: 'cxtrend', + this.$cumlativeGraph = $( '<canvas>' ).attr( { + id: 'cxcumulative', + width: this.$container.width() - 200, // Leave a 200px margin buffer to avoid overflow + height: 400 + } ); + + this.$languageCumulativeGraph = $( '<canvas>' ).attr( { + id: 'cxlangcumulative', width: this.$container.width() - 200, // Leave a 200px margin buffer to avoid overflow height: 400 } ); @@ -42,18 +48,32 @@ $spinner, this.$highlights, $( '<h2>' ).text( mw.msg( 'cx-stats-published-translations-title' ) ), - $( '<div>' ).addClass( 'cx-stats-trend' ).append( this.$graph ) + $( '<div>' ) + .addClass( 'cx-stats-graph cx-stats-cumulative-total' ) + .append( this.$cumlativeGraph ), + $( '<h2>' ).text( mw.message( + 'cx-trend-translations-to', + $.uls.data.getAutonym( mw.config.get( 'wgContentLanguage' ) ) + ).escaped() ), + $( '<div>' ) + .addClass( 'cx-stats-graph cx-stats-cumulative-lang' ) + .append( this.$languageCumulativeGraph ) ); $.when( this.getCXTrends(), this.getCXTrends( mw.config.get( 'wgContentLanguage' ) ) ).done( function ( totalTrend, languageTrend ) { - self.totalTranslationTrend = totalTrend; - self.languageTranslationTrend = languageTrend; - self.languageTranslationTrend = mergeAndFill( totalTrend, languageTrend ); + self.totalTranslationTrend = totalTrend.translations; + self.languageTranslationTrend = languageTrend.translations; + self.languageTranslationTrend = mergeAndFill( self.totalTranslationTrend, self.languageTranslationTrend ); + + self.totalDraftTrend = mergeAndFill( self.totalTranslationTrend, totalTrend.drafts ); + self.languageDraftTrend = mergeAndFill( self.languageTranslationTrend, languageTrend.drafts ); + self.languageDraftTrend = mergeAndFill( self.totalDraftTrend, self.languageDraftTrend ); self.renderHighlights(); - self.drawGraph( 'count' ); + self.drawCumulativeGraph( 'count' ); + self.drawLanguageCumulativeGraph( 'count' ); } ); this.getCXStats().then( function ( data ) { if ( !data || !data.query ) { @@ -127,8 +147,7 @@ 'cx-stats-local-published-number', fmt( langTotal ), fmt( localLanguage ) - ) - ) + ) ) ); weekLangTrendText = mw.msg( 'percent', fmt( weekLangTrend ) ); @@ -368,7 +387,9 @@ $total = $( '<a>' ) .addClass( 'cx-stats-chart__total' ) .prop( 'href', mw.cx.siteMapper.getPageUrl( - model[ i ].language, 'Special:NewPages', { tagfilter: 'contenttranslation' } + model[ i ].language, 'Special:NewPages', { + tagfilter: 'contenttranslation' + } ) ) .text( fmt( model[ i ][ property ] ) ); } else { @@ -424,10 +445,10 @@ } ); }; - CXStats.prototype.drawGraph = function ( type ) { - var data, cxTrendGraph, ctx; + CXStats.prototype.drawCumulativeGraph = function ( type ) { + var data, cxCumulativeGraph, ctx; - ctx = this.$graph[ 0 ].getContext( '2d' ); + ctx = this.$cumlativeGraph[ 0 ].getContext( '2d' ); data = { labels: $.map( this.totalTranslationTrend, function ( data ) { @@ -435,34 +456,88 @@ } ), datasets: [ { - label: mw.message( 'cx-trend-all-translations' ).escaped(), - strokeColor: '#FD6E8A', - pointColor: '#FD6E8A', + label: mw.msg( 'cx-trend-all-translations' ), + fillColor: '#347BFF', + strokeColor: '#347BFF', + pointColor: '#347BFF', + pointStrokeColor: '#fff', + pointHighlightFill: '#fff', + pointHighlightStroke: '#347BFF', data: $.map( this.totalTranslationTrend, function ( data ) { - return data[type]; + return data[ type ]; } ) }, { - label: mw.message( - 'cx-trend-translations-to', - $.uls.data.getAutonym( mw.config.get( 'wgContentLanguage' ) ) - ).escaped(), - strokeColor: '#80B3FF', - pointColor: '#80B3FF', - data: $.map( this.languageTranslationTrend, function ( data ) { - return data[type]; + label: mw.msg( 'cx-stats-draft-translations-title' ), + fillColor: '#777', + strokeColor: '#777', + pointColor: '#777', + pointStrokeColor: '#fff', + pointHighlightFill: '#fff', + pointHighlightStroke: '#777', + data: $.map( this.totalDraftTrend, function ( data ) { + return data[ type ]; } ) } ] }; /*global Chart:false */ - cxTrendGraph = new Chart( ctx ).Line( data, { + cxCumulativeGraph = new Chart( ctx ).Line( data, { + responsive: true, datasetFill: false, legendTemplate: '<ul><% for (var i=0; i<datasets.length; i++){%><li style=\"color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>' } ); - this.$container.find( '.cx-stats-trend' ).append( cxTrendGraph.generateLegend() ); + this.$container.find( '.cx-stats-cumulative-total' ).append( cxCumulativeGraph.generateLegend() ); + }; + + CXStats.prototype.drawLanguageCumulativeGraph = function ( type ) { + var data, cxCumulativeGraph, ctx; + + ctx = this.$languageCumulativeGraph[ 0 ].getContext( '2d' ); + + data = { + labels: $.map( this.totalTranslationTrend, function ( data ) { + return data.date; + } ), + datasets: [ + { + label: mw.message( + 'cx-trend-translations-to', + $.uls.data.getAutonym( mw.config.get( 'wgContentLanguage' ) ) + ).escaped(), + strokeColor: '#347BFF', + pointColor: '#347BFF', + pointStrokeColor: '#fff', + pointHighlightFill: '#fff', + pointHighlightStroke: '#347BFF', + data: $.map( this.languageTranslationTrend, function ( data ) { + return data[ type ]; + } ) + }, + { + label: mw.msg( 'cx-stats-draft-translations-title' ), + strokeColor: '#777', + pointColor: '#777', + pointStrokeColor: '#fff', + pointHighlightFill: '#fff', + pointHighlightStroke: '#777', + data: $.map( this.languageDraftTrend, function ( data ) { + return data[ type ]; + } ) + } + ] + }; + + /*global Chart:false */ + cxCumulativeGraph = new Chart( ctx ).Line( data, { + datasetFill: false, + responsive: true, + legendTemplate: '<ul><% for (var i=0; i<datasets.length; i++){%><li style=\"color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>' + } ); + + this.$container.find( '.cx-stats-cumulative-lang' ).append( cxCumulativeGraph.generateLegend() ); }; CXStats.prototype.transformJsonToModel = function ( records ) { @@ -557,8 +632,7 @@ * @return {[Object]} Array of translations to a particular language, after padding */ function mergeAndFill( totalData, languageData ) { - var i, - padding = []; + var i; if ( totalData.length === languageData.length ) { return languageData; @@ -570,23 +644,18 @@ if ( !languageData || languageData.length === 0 ) { break; } - - if ( totalData[ i ].date === languageData[ 0 ].date ) { - languageData = padding.concat( languageData ); - } else { - padding.push( { - date: totalData[ i ].date, - count: 0 + if ( languageData[ i ] && new Date( totalData[ i ].date ) > new Date( languageData[ i ].date ) ) { + totalData.splice( i, 0, { + date: languageData[ i ].date, + count: totalData[ i - 1 ] ? totalData[ i - 1 ].count : 0 } ); } - } - - // Fill at the end if languageData is shorter than totalData - for ( i = languageData.length; i < totalData.length; i++ ) { - languageData.push( { - date: totalData[ i ].date, - count: languageData.length ? languageData[ i - 1 ].count : 0 - } ); + if ( !languageData[ i ] || new Date( totalData[ i ].date ) < new Date( languageData[ i ].date ) ) { + languageData.splice( i, 0, { + date: totalData[ i ].date, + count: languageData[ i - 1 ] ? languageData[ i - 1 ].count : 0 + } ); + } } return languageData; diff --git a/modules/stats/styles/ext.cx.stats.less b/modules/stats/styles/ext.cx.stats.less index c5ef8b9..02b43fb 100644 --- a/modules/stats/styles/ext.cx.stats.less +++ b/modules/stats/styles/ext.cx.stats.less @@ -150,7 +150,7 @@ text-align: left; } -.cx-stats-trend { +.cx-stats-graph { background-color: #fff; } -- To view, visit https://gerrit.wikimedia.org/r/236529 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ifd4bd0e3a92ac022428edf3915552ea99142dbfb Gerrit-PatchSet: 4 Gerrit-Project: mediawiki/extensions/ContentTranslation Gerrit-Branch: master Gerrit-Owner: Santhosh <santhosh.thottin...@gmail.com> Gerrit-Reviewer: Nikerabbit <niklas.laxst...@gmail.com> Gerrit-Reviewer: Santhosh <santhosh.thottin...@gmail.com> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits