forked from OPSnet/Gazelle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
UserRank.php
97 lines (87 loc) · 3.22 KB
/
UserRank.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?php
namespace Gazelle;
/* The purpose of the UserRank classes is to add a level of abstraction
* to the calculation of user ranks (most uploaded, most forum posts).
* The aim is to drive as much as possible from a static configuration
* table and have the code do its work, to allow other users of Gazelle
* to add different dimensions to ranks without have to monkey patch
* the internals.
*
* It begins with a RANKING_WEIGHT table in the configuration, which
* specifies the weight a dimension has towards the overall score, and
* a class name X that points to \Gazelle\UserRank\X.
*
* To explore and test in Boris:
* Consider that there are two users, one who has up/down votes a
* single release, and another who has voted on two:
*
* > $config = new Gazelle\UserRank\Configuration(RANKING_WEIGHT);
* > $config->instance('votes')->build();
* // array(
* // 0 => 1,
* // 1 => 2
* // )
* > $config->instance('votes')->rank(0);
* // 0
* > $config->instance('votes')->rank(1);
* // 50
* > $config->instance('votes')->rank(2);
* // 100
* > $config->instance('votes')->rank(3);
* // 100
*
* The UserRank object adds a wrapper over the top of the config
* object. It ignores the notion of paranoia, so metrics must be
* set to 0 when calculating the rank of paranoid people.
*
* Adding a new dimension should be as simple as adding an entry to the
* RANKING_WEIGHT table and writing a new class in app/UserRank/<whatever>.php
* This then has to hooked up to sections/user/user.php and
* sections/ajax/user.php
*
* Future directions: pass a \Gazelle\User object to the UserRank
* object, and define the appropriate ethod names in the ranking
* table so that the dimension classes can obtain the metrics
* directly and not need to have them passed in.
*/
class UserRank extends Base {
protected array $rank;
protected float $score = 0.0;
final const PREFIX = 'percentiles_'; // Prefix for memcache keys, to make life easier
public function score(): ?int {
return is_nan($this->score) ? null : (int)round($this->score, 0);
}
public function rank(string $dimension): int {
return $this->rank[$dimension];
}
public function __construct(protected \Gazelle\UserRank\Configuration $config, array $dimension) {
$definition = $this->config->definition();
$dimension['uploaded'] -= STARTING_UPLOAD;
$ok = true;
foreach ($definition as $d) {
$this->rank[$d] = $this->config->instance($d)->rank($dimension[$d]);
if ($this->rank[$d] == false) {
$ok = false;
}
}
if (!$ok) {
$this->score = NAN;
return;
}
$totalWeight = 0.0;
foreach ($definition as $d) {
$weight = $this->config->weight($d);
$this->score += $weight * $this->rank[$d];
$totalWeight += $weight;
}
$this->score /= $totalWeight;
if ($dimension['downloaded'] == 0) {
$ratio = 1;
} elseif ($dimension['uploaded'] == 0) {
$ratio = 0.5;
} else {
$ratio = min(1, round($dimension['uploaded'] / $dimension['downloaded']));
}
$this->score *= $ratio;
}
}