Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
54.84% |
17 / 31 |
|
14.29% |
1 / 7 |
CRAP | |
0.00% |
0 / 1 |
ExperimentUserManager | |
54.84% |
17 / 31 |
|
14.29% |
1 / 7 |
22.15 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
setPlatform | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getVariant | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
setVariant | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
isUserInVariant | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isValidVariant | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRandomVariant | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
4.01 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments; |
4 | |
5 | use MediaWiki\Config\ServiceOptions; |
6 | use MediaWiki\User\Options\UserOptionsLookup; |
7 | use MediaWiki\User\Options\UserOptionsManager; |
8 | use MediaWiki\User\UserIdentity; |
9 | |
10 | /** |
11 | * Service for handling experiment / variant related functions for users. |
12 | */ |
13 | class ExperimentUserManager { |
14 | |
15 | private ServiceOptions $options; |
16 | private UserOptionsLookup $userOptionsLookup; |
17 | private UserOptionsManager $userOptionsManager; |
18 | |
19 | /** @var string|null One of 'mobile' or 'desktop' */ |
20 | private ?string $platform; |
21 | |
22 | public const CONSTRUCTOR_OPTIONS = [ |
23 | 'GEHomepageDefaultVariant', |
24 | 'GEHomepageNewAccountVariantsByPlatform', |
25 | ]; |
26 | |
27 | /** |
28 | * @param ServiceOptions $options |
29 | * @param UserOptionsManager $userOptionsManager |
30 | * @param UserOptionsLookup $userOptionsLookup |
31 | * @param string|null $platform |
32 | */ |
33 | public function __construct( |
34 | ServiceOptions $options, |
35 | UserOptionsManager $userOptionsManager, |
36 | UserOptionsLookup $userOptionsLookup, |
37 | ?string $platform = null |
38 | ) { |
39 | $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
40 | $this->options = $options; |
41 | $this->userOptionsLookup = $userOptionsLookup; |
42 | $this->userOptionsManager = $userOptionsManager; |
43 | $this->platform = $platform; |
44 | } |
45 | |
46 | /** |
47 | * Specify if the experiment manager is in a desktop/mobile platform context. |
48 | * |
49 | * @param string $platform One of "mobile" or "desktop" |
50 | */ |
51 | public function setPlatform( string $platform ): void { |
52 | $this->platform = $platform; |
53 | } |
54 | |
55 | /** |
56 | * @param UserIdentity $user |
57 | * @return string |
58 | */ |
59 | public function getVariant( UserIdentity $user ): string { |
60 | $variant = $this->userOptionsLookup->getOption( |
61 | $user, |
62 | VariantHooks::USER_PREFERENCE |
63 | ); |
64 | if ( !in_array( $variant, VariantHooks::VARIANTS ) ) { |
65 | $variant = $this->options->get( 'GEHomepageDefaultVariant' ); |
66 | } |
67 | return $variant; |
68 | } |
69 | |
70 | /** |
71 | * Set (but does not save) the variant for a user. |
72 | * |
73 | * @param UserIdentity $user |
74 | * @param string $variant |
75 | */ |
76 | public function setVariant( UserIdentity $user, string $variant ): void { |
77 | $this->userOptionsManager->setOption( |
78 | $user, |
79 | VariantHooks::USER_PREFERENCE, |
80 | $variant |
81 | ); |
82 | } |
83 | |
84 | /** |
85 | * @param UserIdentity $user |
86 | * @param string|string[] $variant |
87 | * @return bool |
88 | */ |
89 | public function isUserInVariant( UserIdentity $user, $variant ): bool { |
90 | return in_array( $this->getVariant( $user ), (array)$variant ); |
91 | } |
92 | |
93 | /** |
94 | * @param string $variant |
95 | * @return bool |
96 | */ |
97 | public function isValidVariant( string $variant ): bool { |
98 | return in_array( $variant, VariantHooks::VARIANTS ); |
99 | } |
100 | |
101 | /** |
102 | * Get a random variant according to the distribution defined in $wgGEHomepageNewAccountVariantsByPlatform. |
103 | * |
104 | * @return string |
105 | */ |
106 | public function getRandomVariant(): string { |
107 | $variantProbabilities = $this->options->get( 'GEHomepageNewAccountVariantsByPlatform' ); |
108 | $random = rand( 0, 99 ); |
109 | |
110 | $variant = $this->options->get( 'GEHomepageDefaultVariant' ); |
111 | foreach ( $variantProbabilities as $candidateVariant => $percentageForVariant ) { |
112 | if ( !$this->isValidVariant( $candidateVariant ) ) { |
113 | continue; |
114 | } |
115 | if ( $random < $percentageForVariant[$this->platform] ) { |
116 | $variant = $candidateVariant; |
117 | break; |
118 | } |
119 | $random -= $percentageForVariant[$this->platform]; |
120 | } |
121 | return $variant; |
122 | } |
123 | } |