committee.gno
3.13 Kb ยท 140 lines
1package evaluation
2
3import (
4 "chain/runtime"
5
6 "gno.land/p/nt/avl"
7 "gno.land/p/nt/ufmt"
8)
9
10type Committee struct {
11 members []address // TODO - use avl tree or address set?
12 categories avl.Tree // A category is mapped to a list of evaluation criteria
13 evaluation *Evaluation
14}
15
16const ApprovedStatus = "Approved"
17
18func NewCommittee() *Committee {
19 c := &Committee{
20 members: []address{},
21 categories: avl.Tree{},
22 evaluation: NewEvalutaion(),
23 }
24 return c
25}
26
27func (c *Committee) DesignateMembers(members []address) []address {
28 c.members = append(c.members, members...)
29 return c.members
30}
31
32func (c *Committee) DismissMembers(members []address) []address {
33 // TODO
34 return []address{}
35}
36
37func (c *Committee) AddCategory(name string, criteria []string) bool {
38 // TODO error handling
39 if !c.isMember(runtime.OriginCaller()) {
40 return false
41 }
42 category := NewCategory(name, criteria)
43 c.categories.Set(name, category)
44 return true
45}
46
47func (c *Committee) ApproveCategory(name string, option string) bool {
48 if !c.isMember(runtime.OriginCaller()) {
49 return false
50 }
51
52 value, exists := c.categories.Get(name)
53 if !exists {
54 return false
55 }
56 category := value.(*Category)
57 if category.Status() == ApprovedStatus {
58 return false
59 }
60
61 vote := NewVote(runtime.OriginCaller(), option)
62 category.votes.Set(runtime.OriginCaller().String(), vote)
63 category.Tally()
64
65 // TODO Add threshold factor for a category approval
66 // TODO Add quorum factor for a category approval
67 // Current assumption is all members voted YES so category is approved
68
69 result, exists := category.tallyResult.results.Get(VoteYes)
70 if !exists {
71 return false
72 }
73
74 if result.(int) == len(c.members) {
75 category.Approve()
76 return true
77 }
78
79 return false
80}
81
82// TODO error handling
83func (c *Committee) AddContribution(pr *PullRequest, contributor address) (contributionId int, ok bool) {
84 if !c.isMember(runtime.OriginCaller()) {
85 return -1, false
86 }
87 // Check the category of the PR matches a category this committee evaluates
88 // TODO check the category is an approved category
89 if c.categories.Has(pr.category) {
90 return c.evaluation.AddContribution(pr, contributor)
91 }
92
93 return -1, false
94}
95
96// TODO error handling
97func (c *Committee) ApproveContribution(id int, option string) bool {
98 if !c.isMember(runtime.OriginCaller()) {
99 return false
100 }
101
102 value, exists := c.evaluation.contributions.Get(ufmt.Sprintf("%d", id))
103 if !exists {
104 return false
105 }
106 contribution := value.(*Contribution)
107 // Already approved
108 if contribution.status == ApprovedStatus {
109 return false
110 }
111
112 vote := NewVote(runtime.OriginCaller(), option)
113 contribution.votes = append(contribution.votes, vote)
114 contribution.Tally()
115
116 // TODO Add threshold factor for a contribution approval
117 // TODO Add quorum factor for a contribution approval
118 // Current assumption is all members voted YES so contribution is approved
119
120 result, exists := contribution.tallyResult.results.Get(VoteYes)
121 if !exists {
122 return false
123 }
124
125 if result.(int) == len(c.members) {
126 contribution.Approve()
127 return true
128 }
129
130 return false
131}
132
133func (c *Committee) isMember(m address) bool {
134 for _, member := range c.members {
135 if m == member {
136 return true
137 }
138 }
139 return false
140}