Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslatorActivity.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\Statistics;
5
6use BagOStuff;
7use InvalidArgumentException;
8use JobQueueGroup;
9use PoolCounterWorkViaCallback;
11use Wikimedia\Timestamp\ConvertibleTimestamp;
12
21 public const CACHE_TIME = 3 * 24 * 3600;
22 // 25 hours so that it's easy to configure the maintenance script run daily
23 public const CACHE_STALE = 25 * 3600;
24 private $cache;
25 private $query;
26 private $jobQueue;
27
28 public function __construct(
29 BagOStuff $cache,
31 JobQueueGroup $jobQueue
32 ) {
33 $this->cache = $cache;
34 $this->query = $query;
35 $this->jobQueue = $jobQueue;
36 }
37
42 public function inLanguage( string $language ): array {
43 if ( !$this->isValidLanguage( $language ) ) {
44 throw new InvalidArgumentException( "Invalid language tag '$language'" );
45 }
46
47 $cachedValue = $this->getFromCache( $language );
48
49 if ( is_array( $cachedValue ) ) {
50 if ( $this->isStale( $cachedValue ) ) {
51 $this->queueCacheRefresh( $language );
52 }
53
54 return $cachedValue;
55 }
56
57 $queriedValue = $this->doQueryAndCache( $language );
58 if ( !$queriedValue ) {
59 throw new StatisticsUnavailable( "Unable to load stats" );
60 }
61
62 return $queriedValue;
63 }
64
65 private function getFromCache( string $language ) {
66 $cacheKey = $this->getCacheKey( $language );
67 return $this->cache->get( $cacheKey );
68 }
69
70 private function getCacheKey( string $language ): string {
71 return $this->cache->makeKey( 'translate-translator-activity-v4', $language );
72 }
73
74 private function isStale( array $value ): bool {
75 $age = intval( ConvertibleTimestamp::now( TS_UNIX ) ) - $value['asOfTime'];
76 return $age >= self::CACHE_STALE;
77 }
78
79 private function queueCacheRefresh( string $language ): void {
80 $job = UpdateTranslatorActivityJob::newJobForLanguage( $language );
81 $this->jobQueue->push( $job );
82 }
83
84 private function doQueryAndCache( string $language ) {
85 $now = (int)ConvertibleTimestamp::now( TS_UNIX );
86
87 $work = new PoolCounterWorkViaCallback(
88 'TranslateFetchTranslators', "TranslateFetchTranslators-$language", [
89 'doWork' => function () use ( $language, $now ) {
90 $users = $this->query->inLanguage( $language );
91 $data = [ 'users' => $users, 'asOfTime' => $now ];
92 $this->addToCache( $data, $language );
93 return $data;
94 },
95 'doCachedWork' => function () use ( $language ) {
96 $data = $this->getFromCache( $language );
97 // Use new cache value from other thread
98 return is_array( $data ) ? $data : false;
99 },
100 ]
101 );
102
103 return $work->execute();
104 }
105
106 private function addToCache( array $value, string $language ): void {
107 $cacheKey = $this->getCacheKey( $language );
108 $this->cache->set( $cacheKey, $value, self::CACHE_TIME );
109 }
110
112 public function updateAllLanguages(): void {
113 $now = (int)ConvertibleTimestamp::now( TS_UNIX );
114
115 $data = $this->query->inAllLanguages();
116 // In case there is no activity for a supported languages, cache empty results
117 $validLanguages = TranslateUtils::getLanguageNames( null );
118 foreach ( $validLanguages as $language ) {
119 $data[$language] = $data[$language] ?? [];
120 }
121
122 foreach ( $data as $language => $users ) {
123 if ( !$this->isValidLanguage( $language ) ) {
124 continue;
125 }
126
127 $data = [ 'users' => $users, 'asOfTime' => $now ];
128 $this->addToCache( $data, $language );
129 }
130 }
131
136 public function updateLanguage( string $language ): void {
137 if ( !$this->isValidLanguage( $language ) ) {
138 throw new InvalidArgumentException( "Invalid language tag '$language'" );
139 }
140
141 $queriedValue = $this->doQueryAndCache( $language );
142 if ( !$queriedValue ) {
143 throw new StatisticsUnavailable( 'Unable to load stats' );
144 }
145 }
146
147 private function isValidLanguage( string $language ): bool {
148 return TranslateUtils::isSupportedLanguageCode( $language );
149 }
150}
return[ 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->getMainConfig(), $services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), MessageIndex::singleton());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore(new RevTagStore(), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, 'Translate:MessageGroupReview'=> static function(MediaWikiServices $services):MessageGroupReview { return new MessageGroupReview($services->getDBLoadBalancer(), $services->getHookContainer());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=$services->getMainConfig() ->get( 'TranslateMessageIndex');if(is_string( $params)) { $params=(array) $params;} $class=array_shift( $params);return new $class( $params);}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getDBLoadBalancer(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'));}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), new RevTagStore(), $services->getDBLoadBalancer());}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnectionRef(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array
updateLanguage(string $language)
Update cache for one language, even if not stale.
inLanguage(string $language)
Get translations activity for a given language.
updateAllLanguages()
Update cache for all languages, even if not stale.
Essentially random collection of helper functions, similar to GlobalFunctions.php.