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;
10use MediaWiki\Languages\LanguageNameUtils;
11use PoolCounterWorkViaCallback;
12use Wikimedia\Timestamp\ConvertibleTimestamp;
13
22 public const CACHE_TIME = 3 * 24 * 3600;
23 // 25 hours so that it's easy to configure the maintenance script run daily
24 public const CACHE_STALE = 25 * 3600;
25 private BagOStuff $cache;
26 private TranslatorActivityQuery $query;
27 private JobQueueGroup $jobQueue;
28
29 public function __construct(
30 BagOStuff $cache,
32 JobQueueGroup $jobQueue
33 ) {
34 $this->cache = $cache;
35 $this->query = $query;
36 $this->jobQueue = $jobQueue;
37 }
38
43 public function inLanguage( string $language ): array {
44 if ( !$this->isValidLanguage( $language ) ) {
45 throw new InvalidArgumentException( "Invalid language tag '$language'" );
46 }
47
48 $cachedValue = $this->getFromCache( $language );
49
50 if ( is_array( $cachedValue ) ) {
51 if ( $this->isStale( $cachedValue ) ) {
52 $this->queueCacheRefresh( $language );
53 }
54
55 return $cachedValue;
56 }
57
58 $queriedValue = $this->doQueryAndCache( $language );
59 if ( !$queriedValue ) {
60 throw new StatisticsUnavailable( "Unable to load stats" );
61 }
62
63 return $queriedValue;
64 }
65
66 private function getFromCache( string $language ) {
67 $cacheKey = $this->getCacheKey( $language );
68 return $this->cache->get( $cacheKey );
69 }
70
71 private function getCacheKey( string $language ): string {
72 return $this->cache->makeKey( 'translate-translator-activity-v4', $language );
73 }
74
75 private function isStale( array $value ): bool {
76 $age = intval( ConvertibleTimestamp::now( TS_UNIX ) ) - $value['asOfTime'];
77 return $age >= self::CACHE_STALE;
78 }
79
80 private function queueCacheRefresh( string $language ): void {
81 $job = UpdateTranslatorActivityJob::newJobForLanguage( $language );
82 $this->jobQueue->push( $job );
83 }
84
85 private function doQueryAndCache( string $language ) {
86 $now = (int)ConvertibleTimestamp::now( TS_UNIX );
87
88 $work = new PoolCounterWorkViaCallback(
89 'TranslateFetchTranslators', "TranslateFetchTranslators-$language", [
90 'doWork' => function () use ( $language, $now ) {
91 $users = $this->query->inLanguage( $language );
92 $data = [ 'users' => $users, 'asOfTime' => $now ];
93 $this->addToCache( $data, $language );
94 return $data;
95 },
96 'doCachedWork' => function () use ( $language ) {
97 $data = $this->getFromCache( $language );
98 // Use new cache value from other thread
99 return is_array( $data ) ? $data : false;
100 },
101 ]
102 );
103
104 return $work->execute();
105 }
106
107 private function addToCache( array $value, string $language ): void {
108 $cacheKey = $this->getCacheKey( $language );
109 $this->cache->set( $cacheKey, $value, self::CACHE_TIME );
110 }
111
113 public function updateAllLanguages(): void {
114 $now = (int)ConvertibleTimestamp::now( TS_UNIX );
115
116 $data = $this->query->inAllLanguages();
117 // In case there is no activity for a supported languages, cache empty results
118 $validLanguages = Utilities::getLanguageNames( LanguageNameUtils::AUTONYMS );
119 foreach ( $validLanguages as $language ) {
120 $data[$language] = $data[$language] ?? [];
121 }
122
123 foreach ( $data as $language => $users ) {
124 if ( !$this->isValidLanguage( $language ) ) {
125 continue;
126 }
127
128 $data = [ 'users' => $users, 'asOfTime' => $now ];
129 $this->addToCache( $data, $language );
130 }
131 }
132
137 public function updateLanguage( string $language ): void {
138 if ( !$this->isValidLanguage( $language ) ) {
139 throw new InvalidArgumentException( "Invalid language tag '$language'" );
140 }
141
142 $queriedValue = $this->doQueryAndCache( $language );
143 if ( !$queriedValue ) {
144 throw new StatisticsUnavailable( 'Unable to load stats' );
145 }
146 }
147
148 private function isValidLanguage( string $language ): bool {
149 return Utilities::isSupportedLanguageCode( $language );
150 }
151}
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'), $services->get( 'Translate:MessageIndex'));}, 'Translate:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getDBLoadBalancer(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, '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:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, '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:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore($services->getDBLoadBalancerFactory());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup());}, '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:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getDBLoadBalancer() ->getConnection(DB_PRIMARY), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, '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(), $services->get( 'Translate:RevTagStore'), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'),);}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnection(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(), $services->getDBLoadBalancer());}, '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.
Definition Utilities.php:31