Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
LanguageEditStatsMaintenanceScript
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 3
210
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
110
 translationChanges
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\Statistics;
5
6use MediaWiki\Extension\Translate\SystemUsers\FuzzyBot;
7use MediaWiki\Extension\Translate\Utilities\BaseMaintenanceScript;
8use MediaWiki\Extension\Translate\Utilities\Utilities;
9use MediaWiki\MediaWikiServices;
10
11/**
12 * Shows a top list of language codes with edits in a given time period
13 *
14 * @author Niklas Laxström
15 * @author Siebrand Mazeland
16 * @copyright Copyright © 2008-2010 Niklas Laxström, Siebrand Mazeland
17 * @license GPL-2.0-or-later
18 * @ingroup Script Stats
19 */
20class LanguageEditStatsMaintenanceScript extends BaseMaintenanceScript {
21    public function __construct() {
22        parent::__construct();
23        $this->addDescription( 'Script to show number of edits per language for all message groups.' );
24        $this->addOption(
25            'top',
26            '(optional) Show given number of language codes (default: 10)',
27            self::OPTIONAL,
28            self::HAS_ARG
29        );
30        $this->addOption(
31            'days',
32            '(optional) Calculate for given number of days (default: 7)',
33            self::OPTIONAL,
34            self::HAS_ARG
35        );
36        $this->addOption(
37            'bots',
38            '(optional) Include bot edits'
39        );
40        $this->addOption(
41            'ns',
42            '(optional) Comma separated list of namespace IDs',
43            self::OPTIONAL,
44            self::HAS_ARG
45        );
46        $this->requireExtension( 'Translate' );
47    }
48
49    public function execute(): void {
50        $days = (int)$this->getOption( 'days' ) ?: 7;
51        $top = (int)$this->getOption( 'top' ) ?: 10;
52        $bots = $this->hasOption( 'bots' );
53
54        $namespaces = [];
55        if ( $this->hasOption( 'ns' ) ) {
56            $input = explode( ',', $this->getOption( 'ns' ) );
57
58            foreach ( $input as $namespace ) {
59                if ( is_numeric( $namespace ) ) {
60                    $namespaces[] = $namespace;
61                }
62            }
63        }
64
65        // Select set of edits to report on
66        $rows = $this->translationChanges( $days, $bots, $namespaces );
67
68        // Get counts for edits per language code
69        $codes = [];
70        foreach ( $rows as $row ) {
71            [ , $code ] = Utilities::figureMessage( $row );
72
73            if ( !isset( $codes[$code] ) ) {
74                $codes[$code] = 0;
75            }
76
77            $codes[$code]++;
78        }
79
80        // Sort counts and report descending up to $top rows.
81        arsort( $codes );
82        $i = 0;
83        foreach ( $codes as $code => $num ) {
84            if ( $i++ === $top ) {
85                break;
86            }
87
88            $this->output( "$code\t$num\n" );
89        }
90    }
91
92    /**
93     * Fetches recent changes for titles in given namespaces
94     *
95     * @param int $days Number of hours.
96     * @param bool $bots Should bot edits be included.
97     * @param int[] $ns List of namespace IDs.
98     * @return string[] List of recent changes.
99     */
100    private function translationChanges( int $days, bool $bots, array $ns ): array {
101        global $wgTranslateMessageNamespaces;
102        $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
103
104        $cutoff = $dbr->timestamp( time() - ( $days * 24 * 3600 ) );
105
106        $conds = [
107            'rc_timestamp >= ' . $dbr->addQuotes( $cutoff ),
108            'rc_namespace' => $ns ?: $wgTranslateMessageNamespaces,
109            'actor_name <> ' . $dbr->addQuotes( FuzzyBot::getName() )
110        ];
111        if ( $bots ) {
112            $conds['rc_bot'] = 0;
113        }
114
115        return $dbr->newSelectQueryBuilder()
116            ->select( [ 'rc_title' ] )
117            ->from( 'recentchanges' )
118            ->join( 'actor', null, 'actor_id=rc_actor' )
119            ->where( $conds )
120            ->caller( __METHOD__ )
121            ->fetchFieldValues();
122    }
123}