Index: trunk/extensions/Translate/Translate.i18n.php |
— | — | @@ -179,6 +179,8 @@ |
180 | 180 | 'translate-stats-edits' => 'Edits', |
181 | 181 | 'translate-stats-users' => 'Translators', |
182 | 182 | 'translate-stats-registrations' => 'Registrations', |
| 183 | + 'translate-stats-review' => 'Reviews', |
| 184 | + 'translate-stats-reviewers' => 'Reviewers', |
183 | 185 | |
184 | 186 | 'translate-statsf-intro' => 'You can generate simple statistics with this form. |
185 | 187 | All values have upper and lower limits.', |
— | — | @@ -196,6 +198,8 @@ |
197 | 199 | 'translate-statsf-count-edits' => 'Number of edits', |
198 | 200 | 'translate-statsf-count-users' => 'Active translators', |
199 | 201 | 'translate-statsf-count-registrations' => 'New users', |
| 202 | + 'translate-statsf-count-reviews' => 'Translation reviews', |
| 203 | + 'translate-statsf-count-reviewers' => 'Reviewers', |
200 | 204 | 'translate-statsf-language' => 'Comma-separated list of language codes:', |
201 | 205 | 'translate-statsf-group' => 'Comma-separated list of group codes:', |
202 | 206 | 'translate-statsf-submit' => 'Preview', |
Index: trunk/extensions/Translate/specials/SpecialTranslationStats.php |
— | — | @@ -23,6 +23,31 @@ |
24 | 24 | parent::__construct( 'TranslationStats' ); |
25 | 25 | } |
26 | 26 | |
| 27 | + /// @since 2012-03-05 |
| 28 | + protected static $graphs = array( |
| 29 | + 'edits' => 'TranslatePerLanguageStats', |
| 30 | + 'users' => 'TranslatePerLanguageStats', |
| 31 | + 'registrations' => 'TranslateRegistrationStats', |
| 32 | + 'reviews' => 'ReviewPerLanguageStats', |
| 33 | + 'reviewers' => 'ReviewPerLanguageStats', |
| 34 | + ); |
| 35 | + |
| 36 | + /** |
| 37 | + * @since 2012-03-05 |
| 38 | + * @return list |
| 39 | + */ |
| 40 | + public function getGraphTypes() { |
| 41 | + return array_keys( self::$graphs ); |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * @since 2012-03-05 |
| 46 | + * @return string |
| 47 | + */ |
| 48 | + public function getGraphClass( $type ) { |
| 49 | + return self::$graphs[$type]; |
| 50 | + } |
| 51 | + |
27 | 52 | public function execute( $par ) { |
28 | 53 | global $wgOut, $wgRequest; |
29 | 54 | |
— | — | @@ -69,7 +94,7 @@ |
70 | 95 | $opts->validateIntBounds( 'days', 1, 4 ); |
71 | 96 | } |
72 | 97 | |
73 | | - $validCounts = array( 'edits', 'users', 'registrations' ); |
| 98 | + $validCounts = $this->getGraphTypes(); |
74 | 99 | if ( !in_array( $opts['count'], $validCounts ) ) { |
75 | 100 | $opts['count'] = 'edits'; |
76 | 101 | } |
— | — | @@ -135,7 +160,7 @@ |
136 | 161 | $this->eInput( 'start', $opts, 12 ) . |
137 | 162 | $this->eInput( 'days', $opts ) . |
138 | 163 | $this->eRadio( 'scale', $opts, array( 'months', 'weeks', 'days', 'hours' ) ) . |
139 | | - $this->eRadio( 'count', $opts, array( 'edits', 'users', 'registrations' ) ) . |
| 164 | + $this->eRadio( 'count', $opts, $this->getGraphTypes() ) . |
140 | 165 | '<tr><td colspan="2"><hr /></td></tr>' . |
141 | 166 | $this->eLanguage( 'language', $opts ) . |
142 | 167 | $this->eGroup( 'group', $opts ) . |
— | — | @@ -356,11 +381,8 @@ |
357 | 382 | global $wgLang; |
358 | 383 | $dbr = wfGetDB( DB_SLAVE ); |
359 | 384 | |
360 | | - if ( $opts['count'] === 'registrations' ) { |
361 | | - $so = new TranslateRegistrationStats( $opts ); |
362 | | - } else { |
363 | | - $so = new TranslatePerLanguageStats( $opts ); |
364 | | - } |
| 385 | + $class = $this->getGraphClass( $opts['count'] ); |
| 386 | + $so = new $class( $opts ); |
365 | 387 | |
366 | 388 | $fixedStart = $opts->getValue( 'start' ) !== ''; |
367 | 389 | |
— | — | @@ -737,6 +759,20 @@ |
738 | 760 | } |
739 | 761 | return $conds; |
740 | 762 | } |
| 763 | + |
| 764 | + /// @since 2012-03-05 |
| 765 | + protected static function namespacesFromGroups( $groupIds ) { |
| 766 | + $namespaces = array(); |
| 767 | + foreach ( $groupIds as $id ) { |
| 768 | + $group = MessageGroups::getGroup( $id ); |
| 769 | + if ( $group ) { |
| 770 | + $namespace = $group->getNamespace(); |
| 771 | + $namespaces[$namespace] = true; |
| 772 | + } |
| 773 | + } |
| 774 | + |
| 775 | + return array_keys( $namespaces ); |
| 776 | + } |
741 | 777 | } |
742 | 778 | |
743 | 779 | /** |
— | — | @@ -776,27 +812,16 @@ |
777 | 813 | $this->groups = array_filter( array_map( 'trim', explode( ',', $this->opts['group'] ) ) ); |
778 | 814 | $this->codes = array_filter( array_map( 'trim', explode( ',', $this->opts['language'] ) ) ); |
779 | 815 | |
780 | | - $namespaces = array(); |
781 | | - $languages = array(); |
782 | | - |
783 | | - foreach ( $this->groups as $id ) { |
784 | | - $group = MessageGroups::getGroup( $id ); |
785 | | - if ( $group ) { |
786 | | - $namespaces[] = $group->getNamespace(); |
787 | | - } |
| 816 | + $namespaces = self::namespacesFromGroups( $this->groups ); |
| 817 | + if ( count( $namespaces ) ) { |
| 818 | + $conds['rc_namespace'] = $namespaces; |
788 | 819 | } |
789 | 820 | |
| 821 | + $languages = array(); |
790 | 822 | foreach ( $this->codes as $code ) { |
791 | 823 | $languages[] = 'rc_title ' . $db->buildLike( $db->anyString(), "/$code" ); |
792 | 824 | } |
793 | | - |
794 | | - if ( count( $namespaces ) ) { |
795 | | - $namespaces = array_unique( $namespaces ); |
796 | | - $conds['rc_namespace'] = $namespaces; |
797 | | - } |
798 | | - |
799 | 825 | if ( count( $languages ) ) { |
800 | | - $languages = array_unique( $languages ); |
801 | 826 | $conds[] = $db->makeList( $languages, LIST_OR ); |
802 | 827 | } |
803 | 828 | |
— | — | @@ -949,3 +974,107 @@ |
950 | 975 | return $row->user_registration; |
951 | 976 | } |
952 | 977 | } |
| 978 | + |
| 979 | +/** |
| 980 | + * Graph which provides statistics on number of reviews and reviewers. |
| 981 | + * @since 2012-03-05 |
| 982 | + * @ingroup Stats |
| 983 | + */ |
| 984 | +class ReviewPerLanguageStats extends TranslatePerLanguageStats { |
| 985 | + public function preQuery( &$tables, &$fields, &$conds, &$type, &$options, $start, $end ) { |
| 986 | + global $wgTranslateMessageNamespaces; |
| 987 | + |
| 988 | + $db = wfGetDB( DB_SLAVE ); |
| 989 | + |
| 990 | + $tables = array( 'logging' ); |
| 991 | + $fields = array( 'log_timestamp' ); |
| 992 | + |
| 993 | + $conds = array( |
| 994 | + 'log_namespace' => $wgTranslateMessageNamespaces, |
| 995 | + 'log_action' => 'message', |
| 996 | + ); |
| 997 | + |
| 998 | + $timeConds = self::makeTimeCondition( 'log_timestamp', $start, $end ); |
| 999 | + $conds = array_merge( $conds, $timeConds ); |
| 1000 | + |
| 1001 | + $options = array( 'ORDER BY' => 'log_timestamp' ); |
| 1002 | + |
| 1003 | + $this->groups = array_filter( array_map( 'trim', explode( ',', $this->opts['group'] ) ) ); |
| 1004 | + $this->codes = array_filter( array_map( 'trim', explode( ',', $this->opts['language'] ) ) ); |
| 1005 | + |
| 1006 | + $namespaces = self::namespacesFromGroups( $this->groups ); |
| 1007 | + if ( count( $namespaces ) ) { |
| 1008 | + $conds['log_namespace'] = $namespaces; |
| 1009 | + } |
| 1010 | + |
| 1011 | + $languages = array(); |
| 1012 | + foreach ( $this->codes as $code ) { |
| 1013 | + $languages[] = 'log_title ' . $db->buildLike( $db->anyString(), "/$code" ); |
| 1014 | + } |
| 1015 | + if ( count( $languages ) ) { |
| 1016 | + $conds[] = $db->makeList( $languages, LIST_OR ); |
| 1017 | + } |
| 1018 | + |
| 1019 | + $fields[] = 'log_title'; |
| 1020 | + |
| 1021 | + if ( $this->groups ) { |
| 1022 | + $fields[] = 'log_namespace'; |
| 1023 | + } |
| 1024 | + |
| 1025 | + if ( $this->opts['count'] === 'reviewers' ) { |
| 1026 | + $fields[] = 'log_user_text'; |
| 1027 | + } |
| 1028 | + |
| 1029 | + $type .= '-reviews'; |
| 1030 | + } |
| 1031 | + |
| 1032 | + public function indexOf( $row ) { |
| 1033 | + // We need to check that there is only one user per day. |
| 1034 | + if ( $this->opts['count'] === 'reviewers' ) { |
| 1035 | + $date = $this->formatTimestamp( $row->log_timestamp ); |
| 1036 | + |
| 1037 | + if ( isset( $this->usercache[$date][$row->log_user_text] ) ) { |
| 1038 | + return -1; |
| 1039 | + } else { |
| 1040 | + $this->usercache[$date][$row->log_user_text] = 1; |
| 1041 | + } |
| 1042 | + } |
| 1043 | + |
| 1044 | + // Do not consider language-less pages. |
| 1045 | + if ( strpos( $row->log_title, '/' ) === false ) { |
| 1046 | + return false; |
| 1047 | + } |
| 1048 | + |
| 1049 | + // No filters, just one key to track. |
| 1050 | + if ( !$this->groups && !$this->codes ) { |
| 1051 | + return 'all'; |
| 1052 | + } |
| 1053 | + |
| 1054 | + // The key-building needs to be in sync with ::labels(). |
| 1055 | + list( $key, $code ) = TranslateUtils::figureMessage( $row->log_title ); |
| 1056 | + |
| 1057 | + $groups = array(); |
| 1058 | + $codes = array(); |
| 1059 | + |
| 1060 | + if ( $this->groups ) { |
| 1061 | + /* Get list of keys that the message belongs to, and filter |
| 1062 | + * out those which are not requested. */ |
| 1063 | + $groups = TranslateUtils::messageKeyToGroups( $row->log_namespace, $key ); |
| 1064 | + $groups = array_intersect( $this->groups, $groups ); |
| 1065 | + } |
| 1066 | + |
| 1067 | + if ( $this->codes ) { |
| 1068 | + $codes = array( $code ); |
| 1069 | + } |
| 1070 | + |
| 1071 | + return $this->combineTwoArrays( $groups, $codes ); |
| 1072 | + } |
| 1073 | + |
| 1074 | + public function labels() { |
| 1075 | + return $this->combineTwoArrays( $this->groups, $this->codes ); |
| 1076 | + } |
| 1077 | + |
| 1078 | + public function getTimestamp( $row ) { |
| 1079 | + return $row->log_timestamp; |
| 1080 | + } |
| 1081 | +} |