Files
zarinpal-analytics-backend/internal/growth/peer.go

61 lines
1.7 KiB
Go

package growth
import "slices"
type PeerState string
const (
PeerReady PeerState = "READY"
PeerSuppressed PeerState = "SUPPRESSED"
)
type PeerMerchantValue struct {
PaidSessions int64 `json:"paidSessions"`
SessionCount int64 `json:"sessionCount"`
}
type PeerInput struct {
TerminalScoped bool `json:"terminalScoped"`
MerchantPaidSessions int64 `json:"merchantPaidSessions"`
Peers []PeerMerchantValue `json:"peers"`
}
type PeerResult struct {
State PeerState `json:"state"`
Reason SuppressionReason `json:"reason,omitempty"`
EligiblePeerCount int `json:"eligiblePeerCount"`
CombinedPeerSessions int64 `json:"combinedPeerSessions"`
MedianPaidSessions int64 `json:"medianPaidSessions"`
PercentileBPS int64 `json:"percentileBps"`
}
func EvaluatePeer(input PeerInput) PeerResult {
result := PeerResult{State: PeerSuppressed, EligiblePeerCount: len(input.Peers)}
if input.TerminalScoped {
result.Reason = SuppressionTerminalScope
return result
}
values := make([]int64, 0, len(input.Peers))
for _, peer := range input.Peers {
result.CombinedPeerSessions += peer.SessionCount
values = append(values, peer.PaidSessions)
}
if len(values) < 10 || result.CombinedPeerSessions < 1_000 {
result.Reason = SuppressionInsufficientCohort
return result
}
slices.Sort(values)
result.State = PeerReady
result.MedianPaidSessions = median(values)
var below, equal int64
for _, value := range values {
if input.MerchantPaidSessions > value {
below++
} else if input.MerchantPaidSessions == value {
equal++
}
}
result.PercentileBPS = (2*below + equal) * 5_000 / int64(len(values))
return result
}