r97153 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r97152‎ | r97153 | r97154 >
Date:14:14, 15 September 2011
Author:nikerabbit
Status:deferred
Tags:
Comment:
Added MessageGroupStats - database backed caching for counting translated and fuzzy messages
Modified paths:
  • /trunk/extensions/Translate/_autoload.php (modified) (history)
  • /trunk/extensions/Translate/utils/MessageGroupStats.php (added) (history)

Diff [purge]

Index: trunk/extensions/Translate/_autoload.php
@@ -104,6 +104,7 @@
105105 $wgAutoloadClasses['JsSelectToInput'] = $dir . 'utils/JsSelectToInput.php';
106106 $wgAutoloadClasses['HTMLJsSelectToInputField'] = $dir . 'utils/HTMLJsSelectToInputField.php';
107107 $wgAutoloadClasses['MessageGroupCache'] = $dir . 'utils/MessageGroupCache.php';
 108+$wgAutoloadClasses['MessageGroupStats'] = $dir . 'utils/MessageGroupStats.php';
108109 $wgAutoloadClasses['MessageWebImporter'] = $dir . 'utils/MessageWebImporter.php';
109110 $wgAutoloadClasses['TranslationEditPage'] = $dir . 'utils/TranslationEditPage.php';
110111 $wgAutoloadClasses['TranslationHelpers'] = $dir . 'utils/TranslationHelpers.php';
Index: trunk/extensions/Translate/utils/MessageGroupStats.php
@@ -0,0 +1,275 @@
 2+<?php
 3+/**
 4+ * This file aims to provide efficient mechanism for fetching translation completion stats.
 5+ *
 6+ * @file
 7+ * @author Wikia http://trac.wikia-code.com/browser/wikia/trunk/extensions/wikia/TranslationStatistics
 8+ * @author Niklas Laxström
 9+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 3 or later
 10+ */
 11+
 12+/**
 13+ * This class abstract MessageGroup statistics calculation and storing.
 14+ * You can access stats easily per language or per group.
 15+ * Stat array for each item is of format array( total, translate, fuzzy ).
 16+ */
 17+class MessageGroupStats {
 18+ /// Name of the database table
 19+ const TABLE = 'translate_groupstats';
 20+
 21+ /// @var float
 22+ protected static $timeStart = null;
 23+ /// @var float
 24+ protected static $limit = null;
 25+
 26+ /**
 27+ * Set the maximum time statistics are calculated.
 28+ * If the time limit is exceeded, the missing
 29+ * entries will be null.
 30+ * @param $limit float time in seconds
 31+ */
 32+ public static function setTimeLimit( $limit ) {
 33+ self::$timeStart = microtime( true );
 34+ self::$limit = $limit;
 35+ }
 36+
 37+ /**
 38+ * Returns stats for all groups in given language.
 39+ * @param $code string Language code
 40+ * @return Array
 41+ */
 42+ public static function forLanguage( $code ) {
 43+ $stats = self::forLanguageInternal( $code );
 44+ $flattened = array();
 45+ foreach ( $stats as $group => $languages ) {
 46+ $flattened[$group] = $languages[$code];
 47+ }
 48+ return $flattened;
 49+ }
 50+
 51+ /**
 52+ * Returns stats for all languages in given group.
 53+ * @param $code string Group id
 54+ * @return Array
 55+ */
 56+ public static function forGroup( $id ) {
 57+ $group = MessageGroups::getGroup( $id );
 58+ if ( $group === null ) {
 59+ return array();
 60+ }
 61+ $stats = self::forGroupInternal( $group );
 62+ return $stats[$id];
 63+ }
 64+
 65+ /**
 66+ * Returns stats for all group in all languages.
 67+ * Might be slow, might use lots of memory.
 68+ * Returns two dimensional array indexed by group and language.
 69+ * @return Array
 70+ */
 71+ public static function forEverything() {
 72+ $groups = MessageGroups::singleton()->getGroups();
 73+ $stats = array();
 74+ foreach ( $groups as $g ) {
 75+ $stats = self::forGroupInternal( $g, $stats );
 76+ }
 77+ return $stats;
 78+ }
 79+
 80+ /**
 81+ * Purges all cached stats.
 82+ */
 83+ public static function clearAll() {
 84+ $dbw = wfGetDB( DB_MASTER );
 85+ $dbw->delete( self::TABLE, '*' );
 86+ }
 87+
 88+ protected static function extractResults( $res, $stats = array() ) {
 89+ foreach ( $res as $row ) {
 90+ $stats[$row->tgs_group][$row->tgs_lang] = self::extractNumbers( $row );
 91+ }
 92+ return $stats;
 93+ }
 94+
 95+ /**
 96+ * Returns an array of needed database fields.
 97+ */
 98+ protected static function extractNumbers( $row ) {
 99+ return array(
 100+ $row->tgs_total,
 101+ $row->tgs_translated,
 102+ $row->tgs_fuzzy
 103+ );
 104+ }
 105+
 106+ protected static function forLanguageInternal( $code, $stats = array() ) {
 107+ $res = self::selectRowsIdLang( null, $code );
 108+ $stats = self::extractResults( $res, $stats );
 109+
 110+ $groups = MessageGroups::singleton()->getGroups();
 111+ foreach ( $groups as $id => $group ) {
 112+ if ( isset( $stats[$id][$code] ) ) continue;
 113+ $stats[$id][$code] = self::forItemInternal( $stats, $group, $code );
 114+ }
 115+ return $stats;
 116+ }
 117+
 118+ protected static function forGroupInternal( $group, $stats = array() ) {
 119+ $id = $group->getId();
 120+ $res = self::selectRowsIdLang( $id, null );
 121+ $stats = self::extractResults( $res, $stats );
 122+
 123+ # Go over each language filling missing entries
 124+ $languages = array_keys( Language::getLanguageNames() );
 125+ foreach ( $languages as $code ) {
 126+ if ( isset( $stats[$id][$code] ) ) continue;
 127+ $stats[$id][$code] = self::forItemInternal( $stats, $group, $code );
 128+ }
 129+
 130+ return $stats;
 131+ }
 132+
 133+ protected static function selectRowsIdLang( $ids = null, $codes = null ) {
 134+ $conds = array();
 135+ if ( $ids !== null ) {
 136+ $conds['tgs_group'] = $ids;
 137+ }
 138+
 139+ if ( $codes !== null ) {
 140+ $conds['tgs_lang'] = $codes;
 141+ }
 142+
 143+ $dbr = wfGetDB( DB_SLAVE );
 144+ $res = $dbr->select( self::TABLE, '*', $conds, __METHOD__ );
 145+ return $res;
 146+ }
 147+
 148+ protected static function forItemInternal( &$stats, $group, $code ) {
 149+ $id = $group->getId();
 150+
 151+ if ( self::$timeStart !== null && ( microtime( true ) - self::$timeStart ) > self::$limit ) {
 152+ return array( null, null, null );
 153+ }
 154+
 155+ // Might happen when called recursively
 156+ if ( isset( $stats[$id][$code] ) ) {
 157+ return $stats[$id][$code];
 158+ }
 159+
 160+ if ( $group instanceof AggregateMessageGroup ) {
 161+ $aggregates = array( 0, 0, 0 );
 162+ foreach ( $group->getGroups() as $sid => $sgroup ) {
 163+ if ( !isset( $stats[$sid][$code] ) ) {
 164+ $stats[$sid][$code] = self::forItemInternal( $stats, $sgroup, $code );
 165+ }
 166+ self::multiAdd( $aggregates, $stats[$sid][$code] );
 167+ }
 168+ $stats[$id] = $aggregates;
 169+ } else {
 170+ $aggregates = self::calculateGroup( $group, $code );
 171+ }
 172+
 173+ list( $total, $translated, $fuzzy ) = $aggregates;
 174+
 175+ $data = array(
 176+ 'tgs_group' => $id,
 177+ 'tgs_lang' => $code,
 178+ 'tgs_total' => $total,
 179+ 'tgs_translated' => $translated,
 180+ 'tgs_fuzzy' => $fuzzy,
 181+ );
 182+
 183+ $dbw = wfGetDB( DB_MASTER );
 184+ $dbw->insert(
 185+ self::TABLE,
 186+ $data,
 187+ __METHOD__
 188+ );
 189+
 190+ return $aggregates;
 191+ }
 192+
 193+ public static function multiAdd( &$a, $b ) {
 194+ if ( $a[0] === null || $b[0] === null ) {
 195+ return array_fill( 0, count( $a ), null );
 196+ }
 197+ foreach ( $a as $i => &$v ) {
 198+ $v += $b[$i];
 199+ }
 200+ return $a;
 201+ }
 202+
 203+ protected static function calculateGroup( $group, $code ) {
 204+ global $wgTranslateDocumentationLanguageCode;
 205+ # Calculate if missing and store in the db
 206+ $collection = $group->initCollection( $code );
 207+
 208+
 209+ if ( $code === $wgTranslateDocumentationLanguageCode ) {
 210+ $ffs = $group->getFFS();
 211+ if ( $ffs instanceof GettextFFS ) {
 212+ $template = $ffs->read( 'en' );
 213+ $infile = array();
 214+ foreach ( $template['TEMPLATE'] as $key => $data ) {
 215+ if ( isset( $data['comments']['.'] ) ) {
 216+ $infile[$key] = '1';
 217+ }
 218+ }
 219+ $collection->setInFile( $infile );
 220+ }
 221+ }
 222+
 223+ $collection->filter( 'ignored' );
 224+ $collection->filter( 'optional' );
 225+ // Store the count of real messages for later calculation.
 226+ $total = count( $collection );
 227+
 228+ // Count fuzzy first.
 229+ $collection->filter( 'fuzzy' );
 230+ $fuzzy = $total - count( $collection );
 231+
 232+ // Count the completed translations.
 233+ $collection->filter( 'hastranslation', false );
 234+ $translated = count( $collection );
 235+
 236+ return array( $total, $translated, $fuzzy );
 237+ }
 238+
 239+ public static function update( MessageHandle $handle, $changes = array() ) {
 240+ $dbw = wfGetDB( DB_MASTER );
 241+ $conds = array(
 242+ 'tgs_group' => $handle->getGroupIds(),
 243+ 'tgs_lang' => $handle->getCode(),
 244+ );
 245+
 246+ $values = array();
 247+ foreach ( array( 'total', 'translated', 'fuzzy' ) as $type ) {
 248+ if ( !isset( $changes[$type] ) ) {
 249+ $values[] = "tgs_$type=tgs_$type" .
 250+ self::stringifyNumber( $changes[$type] );
 251+ }
 252+ }
 253+
 254+ $dbw->update( self::TABLE, $values, $conds, __METHOD__ );
 255+ }
 256+
 257+ public static function clear( MessageHandle $handle ) {
 258+ $dbw = wfGetDB( DB_MASTER );
 259+ $conds = array(
 260+ 'tgs_group' => $handle->getGroupIds(),
 261+ 'tgs_lang' => $handle->getCode(),
 262+ );
 263+
 264+ $dbw->delete( self::TABLE, $conds, __METHOD__ );
 265+ }
 266+
 267+ /**
 268+ * Converts input to "+2" "-4" type of string.
 269+ * @param $number int
 270+ * @returns string
 271+ */
 272+ protected static function stringifyNumber( $number ) {
 273+ $number = intval( $number );
 274+ return $number < 0 ? "$number" : "+$number";
 275+ }
 276+}
Property changes on: trunk/extensions/Translate/utils/MessageGroupStats.php
___________________________________________________________________
Added: svn:eol-style
1277 + native

Status & tagging log