@@ -77,13 +77,29 @@ fn calc_final_eligible_rankings(
77
77
. collect ( )
78
78
}
79
79
80
+ fn calc_final_ranking_consensus_per_review ( rankings : & [ impl Borrow < VeteranRankingRow > ] ) -> Decimal {
81
+ let rankings_majority = Decimal :: from ( rankings. len ( ) ) / Decimal :: from ( 2 ) ;
82
+ let ranks = rankings. iter ( ) . counts_by ( |r| r. borrow ( ) . score ( ) ) ;
83
+
84
+ match ( ranks. get ( & FilteredOut ) , ranks. get ( & Excellent ) ) {
85
+ ( Some ( filtered_out) , _) if Decimal :: from ( * filtered_out) >= rankings_majority => {
86
+ Decimal :: from ( * filtered_out) / Decimal :: from ( rankings. len ( ) )
87
+ }
88
+ ( _, Some ( excellent) ) if Decimal :: from ( * excellent) > rankings_majority => {
89
+ Decimal :: from ( * excellent) / Decimal :: from ( rankings. len ( ) )
90
+ }
91
+ _ => Decimal :: from ( * ranks. get ( & Good ) . unwrap ( ) ) / Decimal :: from ( rankings. len ( ) ) ,
92
+ }
93
+ }
94
+
80
95
pub fn calculate_veteran_advisors_incentives (
81
96
veteran_rankings : & [ VeteranRankingRow ] ,
82
97
total_rewards : Rewards ,
83
98
rewards_thresholds : EligibilityThresholds ,
84
99
reputation_thresholds : EligibilityThresholds ,
85
100
rewards_mod_args : Vec < ( Decimal , Decimal ) > ,
86
101
reputation_mod_args : Vec < ( Decimal , Decimal ) > ,
102
+ minimum_consensus : Decimal ,
87
103
) -> HashMap < VeteranAdvisorId , VeteranAdvisorIncentive > {
88
104
let final_rankings_per_review = veteran_rankings
89
105
. iter ( )
@@ -92,6 +108,13 @@ pub fn calculate_veteran_advisors_incentives(
92
108
. map ( |( review, rankings) | ( review, calc_final_ranking_per_review ( & rankings) ) )
93
109
. collect :: < BTreeMap < _ , _ > > ( ) ;
94
110
111
+ let final_rankings_consensus_per_review = veteran_rankings
112
+ . iter ( )
113
+ . into_group_map_by ( |ranking| ranking. review_id ( ) )
114
+ . into_iter ( )
115
+ . map ( |( review, rankings) | ( review, calc_final_ranking_consensus_per_review ( & rankings) ) )
116
+ . collect :: < BTreeMap < _ , _ > > ( ) ;
117
+
95
118
let rankings_per_vca = veteran_rankings
96
119
. iter ( )
97
120
. counts_by ( |ranking| ranking. vca . clone ( ) ) ;
@@ -103,7 +126,7 @@ pub fn calculate_veteran_advisors_incentives(
103
126
. get ( & ranking. review_id ( ) )
104
127
. unwrap ( )
105
128
. is_positive ( )
106
- == ranking. score ( ) . is_positive ( )
129
+ == ranking. score ( ) . is_positive ( ) || * final_rankings_consensus_per_review . get ( & ranking . review_id ( ) ) . unwrap ( ) < minimum_consensus
107
130
} )
108
131
. counts_by ( |ranking| ranking. vca . clone ( ) ) ;
109
132
@@ -156,6 +179,8 @@ mod tests {
156
179
const VCA_1 : & str = "vca1" ;
157
180
const VCA_2 : & str = "vca2" ;
158
181
const VCA_3 : & str = "vca3" ;
182
+ const SIMPLE_MINIMUM_CONSENSUS : Decimal = dec ! ( . 5 ) ;
183
+ const QUALIFIED_MINIMUM_CONSENSUS : Decimal = dec ! ( . 7 ) ;
159
184
160
185
struct RandomIterator ;
161
186
impl Iterator for RandomIterator {
@@ -231,6 +256,7 @@ mod tests {
231
256
. into_iter ( )
232
257
. zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
233
258
. collect ( ) ,
259
+ SIMPLE_MINIMUM_CONSENSUS ,
234
260
) ;
235
261
assert ! ( results. get( VCA_1 ) . is_none( ) ) ;
236
262
let res = results. get ( VCA_2 ) . unwrap ( ) ;
@@ -260,6 +286,7 @@ mod tests {
260
286
. into_iter ( )
261
287
. zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
262
288
. collect ( ) ,
289
+ SIMPLE_MINIMUM_CONSENSUS ,
263
290
) ;
264
291
let res1 = results. get ( VCA_1 ) . unwrap ( ) ;
265
292
assert_eq ! ( res1. reputation, 1 ) ;
@@ -283,12 +310,12 @@ mod tests {
283
310
( Rewards :: new ( 8 , 1 ) , Rewards :: ONE , Rewards :: ONE ) ,
284
311
( Rewards :: new ( 9 , 1 ) , Rewards :: new ( 125 , 2 ) , Rewards :: ONE ) ,
285
312
] ;
286
- for ( agreement , reward_modifier, reputation_modifier) in inputs {
313
+ for ( vca3_agreement , reward_modifier, reputation_modifier) in inputs {
287
314
let rankings = ( 0 ..100 )
288
315
. flat_map ( |i| {
289
316
let vcas =
290
317
vec ! [ VCA_1 . to_owned( ) , VCA_2 . to_owned( ) , VCA_3 . to_owned( ) ] . into_iter ( ) ;
291
- let ( good, filtered_out) = if Rewards :: from ( i) < agreement * Rewards :: from ( 100 )
318
+ let ( good, filtered_out) = if Rewards :: from ( i) < vca3_agreement * Rewards :: from ( 100 )
292
319
{
293
320
( 3 , 0 )
294
321
} else {
@@ -297,7 +324,38 @@ mod tests {
297
324
gen_dummy_rankings ( i. to_string ( ) , 0 , good, filtered_out, vcas) . into_iter ( )
298
325
} )
299
326
. collect :: < Vec < _ > > ( ) ;
300
- let results = calculate_veteran_advisors_incentives (
327
+ let results_simple_consensus = calculate_veteran_advisors_incentives (
328
+ & rankings,
329
+ total_rewards,
330
+ 1 ..=200 ,
331
+ 1 ..=200 ,
332
+ THRESHOLDS
333
+ . into_iter ( )
334
+ . zip ( REWARDS_DISAGREEMENT_MODIFIERS . into_iter ( ) )
335
+ . collect ( ) ,
336
+ THRESHOLDS
337
+ . into_iter ( )
338
+ . zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
339
+ . collect ( ) ,
340
+ SIMPLE_MINIMUM_CONSENSUS ,
341
+ ) ;
342
+ let vca3_expected_reward_portion_simple_consensus = vca3_agreement * Rewards :: from ( 100 ) * reward_modifier;
343
+ dbg ! ( vca3_expected_reward_portion_simple_consensus) ;
344
+ dbg ! ( vca3_agreement, reward_modifier, reputation_modifier) ;
345
+ let vca3_expected_rewards_simple_consensus = total_rewards
346
+ / ( Rewards :: from ( 125 * 2 ) + vca3_expected_reward_portion_simple_consensus)
347
+ * vca3_expected_reward_portion_simple_consensus;
348
+ let res_vca3_simple_consensus = results_simple_consensus. get ( VCA_3 ) . unwrap ( ) ;
349
+ assert_eq ! (
350
+ res_vca3_simple_consensus. reputation,
351
+ ( Rewards :: from( 100 ) * vca3_agreement * reputation_modifier)
352
+ . to_u64( )
353
+ . unwrap( )
354
+ ) ;
355
+ assert ! ( are_close( res_vca3_simple_consensus. rewards, vca3_expected_rewards_simple_consensus) ) ;
356
+
357
+
358
+ let results_qualified_consensus = calculate_veteran_advisors_incentives (
301
359
& rankings,
302
360
total_rewards,
303
361
1 ..=200 ,
@@ -310,21 +368,27 @@ mod tests {
310
368
. into_iter ( )
311
369
. zip ( REPUTATION_DISAGREEMENT_MODIFIERS . into_iter ( ) )
312
370
. collect ( ) ,
371
+ QUALIFIED_MINIMUM_CONSENSUS ,
313
372
) ;
314
- let expected_reward_portion = agreement * Rewards :: from ( 100 ) * reward_modifier;
315
- dbg ! ( expected_reward_portion) ;
316
- dbg ! ( agreement, reward_modifier, reputation_modifier) ;
317
- let expected_rewards = total_rewards
318
- / ( Rewards :: from ( 125 * 2 ) + expected_reward_portion)
319
- * expected_reward_portion;
320
- let res = results. get ( VCA_3 ) . unwrap ( ) ;
373
+
374
+ let vca3_expected_reward_portion_qualified_consensus = Rewards :: from ( 100 ) * dec ! ( 1.25 ) ; // low consensus so max reward modifier, agreement ratio doesn't count as all and rankings are all eligible
375
+ dbg ! ( vca3_expected_reward_portion_qualified_consensus) ;
376
+ dbg ! ( vca3_agreement, reward_modifier, reputation_modifier) ;
377
+
378
+ let vca3_expected_rewards_qualified_consensus = total_rewards
379
+ / ( Rewards :: from ( 125 * 2 ) + vca3_expected_reward_portion_qualified_consensus)
380
+ * vca3_expected_reward_portion_qualified_consensus; // 1/3 of the reward
381
+
382
+ let res_vca3_qualified_consensus = results_qualified_consensus. get ( VCA_3 ) . unwrap ( ) ;
383
+
384
+
321
385
assert_eq ! (
322
- res . reputation,
323
- ( Rewards :: from( 100 ) * agreement * reputation_modifier )
386
+ res_vca3_qualified_consensus . reputation,
387
+ ( Rewards :: from( 100 ) ) // all assessment are valid since consensus is low (2/3 < 0.7 )
324
388
. to_u64( )
325
389
. unwrap( )
326
390
) ;
327
- assert ! ( are_close( res . rewards, expected_rewards ) ) ;
391
+ assert ! ( are_close( res_vca3_qualified_consensus . rewards, vca3_expected_rewards_qualified_consensus ) ) ;
328
392
}
329
393
}
330
394
}
0 commit comments