generated from mattermost/mattermost-plugin-starter-template
/
pick.go
119 lines (100 loc) · 2.99 KB
/
pick.go
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
// See License for license information.
package solarlottery
import (
"math"
"github.com/mattermost/mattermost-plugin-solar-lottery/server/sl"
"github.com/mattermost/mattermost-plugin-solar-lottery/server/utils/types"
)
const negligibleWeight = float64(1e-12)
const veryLargeWeight = float64(1e12)
func (f *fill) pickUser(from *sl.Users) *sl.User {
if from.IsEmpty() {
return nil
}
w := NewWeighted()
for _, user := range from.AsArray() {
w.Append(user.MattermostUserID, f.userWeightF(user))
}
return from.Get(w.WeightedRandom(f.rand))
}
func (f *fill) pickRequireRandom() (done bool, picked *sl.Need) {
return f.pickRequireImpl(func(w *weighted) types.ID {
return w.Random(f.rand)
})
}
func (f *fill) pickRequireHighest() (done bool, picked *sl.Need) {
return f.pickRequireImpl(func(w *weighted) types.ID {
return w.Highest()
})
}
func (f *fill) pickRequireWeightedRandom() (done bool, picked *sl.Need) {
return f.pickRequireImpl(func(w *weighted) types.ID {
return w.WeightedRandom(f.rand)
})
}
func (f *fill) pickRequireImpl(idf func(*weighted) types.ID) (done bool, picked *sl.Need) {
w := NewWeighted()
for _, need := range f.require.AsArray() {
if need.Count() <= 0 {
f.require.Delete(need.GetID())
continue
}
id, weight := need.GetID(), f.requireWeight(need)
if id == sl.AnySkill {
weight = negligibleWeight
}
w.Append(id, weight)
}
if w.Len() == 0 {
return true, nil
}
need := f.require.Get(idf(w))
return false, &need
}
func (f *fill) trimRequire() {
for _, need := range f.require.AsArray() {
if need.Count() <= 0 {
f.require.Delete(need.GetID())
}
}
}
func (f *fill) userWeight(user *sl.User) (w float64) {
w = f.poolWeights[user.MattermostUserID]
if w > 0 {
return w
}
defer func() { f.poolWeights[user.MattermostUserID] = w }()
lastServed := user.LastServed.Get(f.r.RotationID)
if lastServed <= 0 {
lastServed = f.r.FillSettings.Beginning.Unix()
}
if lastServed > f.forTime {
// lastServed in the future can happen for newly added users. Return a
// non-0 but very low number. This way if there are any other eligible
// users in the pool this one is not selected. Yet, if all users in the
// pool are new, one of them is picked.
return negligibleWeight
}
return math.Pow(2, float64(f.forTime-lastServed)/float64(f.doublingPeriod))
}
// Counts each user's weight in once for the need itself, and once for each max
// constraint, then averages the number for the need. The idea is that it
// bubbles up hotter needs, particularly those with restrictions and users "past
// due" so that they get filled first.
func (f *fill) requireWeight(need sl.Need) float64 {
var total float64
users := f.requirePools[need.GetID()].AsArray()
if len(users) == 0 {
return veryLargeWeight
}
for _, user := range users {
w := f.userWeight(user)
total += w
}
// Boost needs that have limits
if f.limit.Contains(need.ID) {
total *= 10
}
return total / float64(len(users))
}