451ea08414
web/handlers/standing.go: GET /standing/{reachID} renders standing score
+ bucket + 4-signal progress + Freeholder-eligible badge. Store extensions:
ListRatings/ListVouches/ListSlashes/ComputeStandingScore (simplified, from
locked x/standing/types constants PriorMean/PriorWeight/ComputeDiversityBonus/
GetVoucherWeight/GetStandingBucket — NOT hardcoded)/ComputeFreeholderSignals
(real FreeholderSignals struct + IsFreeholderEligible). Seed: holder-alia
(12 ratings, 4 cats, 1 Vouch, mature Stash -> eligible) vs holder-bryn
(3 ratings, 1 cat, no Vouch, immature -> not eligible). standing_test.go:
computed-from-locked-constants regression guard (PriorMean=4.0, PriorWeight=
10, DiversityBonus4Cats=0.10), Freeholder-eligible badge reflects
IsFreeholderEligible() (the real method), G-026 error lexicon check.
Coverage: store 98.0%, handlers 88.7%.
---ci---
project: oy
phase: 4
milestone: v0.6
status: execute
---/ci---
60 lines
1.7 KiB
Go
60 lines
1.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
standingtypes "github.com/oy/openyield/x/standing/types"
|
|
)
|
|
|
|
// registerStanding wires the Standing + Freeholder signals route (REQ-043).
|
|
func (s *Server) registerStanding(mux *http.ServeMux) {
|
|
mux.HandleFunc("GET /standing/{reachID}", s.handleStanding)
|
|
}
|
|
|
|
// standingViewData is the template data for the Standing screen.
|
|
type standingViewData struct {
|
|
ReachID string
|
|
Found bool
|
|
Score float64
|
|
Bucket standingtypes.StandingBucket
|
|
Ratings []standingtypes.Rating
|
|
Vouches []standingtypes.Vouch
|
|
Slashes []standingtypes.Slash
|
|
Signals standingtypes.FreeholderSignals
|
|
Eligible bool
|
|
MinScore float64
|
|
MinCats int
|
|
}
|
|
|
|
// handleStanding renders the Standing + Freeholder signals progress (REQ-043).
|
|
// Computed from mock Ratings/Vouches/Slashes using the locked x/standing/types
|
|
// constants + GetStandingBucket/ComputeDiversityBonus/GetVoucherWeight; the
|
|
// 4-signal progress via FreeholderSignals.IsFreeholderEligible().
|
|
func (s *Server) handleStanding(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("reachID")
|
|
_, ok := s.Store.GetReach(id)
|
|
if !ok {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
score, bucket := s.Store.ComputeStandingScore(id)
|
|
ratings := s.Store.ListRatings(id)
|
|
vouches := s.Store.ListVouches(id)
|
|
slashes := s.Store.ListSlashes(id)
|
|
signals := s.Store.ComputeFreeholderSignals(id)
|
|
|
|
s.render(w, "standing.html", standingViewData{
|
|
ReachID: id,
|
|
Found: true,
|
|
Score: score,
|
|
Bucket: bucket,
|
|
Ratings: ratings,
|
|
Vouches: vouches,
|
|
Slashes: slashes,
|
|
Signals: signals,
|
|
Eligible: signals.IsFreeholderEligible(),
|
|
MinScore: standingtypes.FreeholderMinStandingScore,
|
|
MinCats: standingtypes.FreeholderMinCategories,
|
|
})
|
|
}
|