1+ //! User reputation tracking.
2+ //!
3+ //! Responsibilities:
4+ //! - Track participation score, course progress, and contribution quality
5+ //! - Compute completion rate in basis points
6+ //! - Emit events on every state change
7+ //! - Expose read-only views for reputation data
8+
19use crate :: events:: {
210 ContributionRatedEvent , CourseProgressUpdatedEvent , ParticipationUpdatedEvent ,
311} ;
@@ -7,102 +15,107 @@ use soroban_sdk::{symbol_short, Address, Env, Symbol};
715const BASIS_POINTS : u32 = 10000 ;
816const REPUTATION : Symbol = symbol_short ! ( "reptn" ) ;
917
10- pub fn update_participation ( env : & Env , user : Address , points : u32 ) {
11- user. require_auth ( ) ;
12- let mut reputation = get_reputation ( env, & user) ;
13- reputation. participation_score += points;
14- reputation. last_update = env. ledger ( ) . timestamp ( ) ;
15- set_reputation ( env, & user, & reputation) ;
16-
17- // Emit event
18- ParticipationUpdatedEvent {
19- user : user. clone ( ) ,
20- points_added : points,
21- new_participation_score : reputation. participation_score ,
22- updated_at : env. ledger ( ) . timestamp ( ) ,
18+ /// Manages user reputation scores.
19+ pub struct ReputationManager ;
20+
21+ impl ReputationManager {
22+ // ===== Mutations =====
23+
24+ /// Add participation points for a user.
25+ pub fn update_participation ( env : & Env , user : Address , points : u32 ) {
26+ user. require_auth ( ) ;
27+ let mut reputation = Self :: get_reputation ( env, & user) ;
28+ reputation. participation_score += points;
29+ reputation. last_update = env. ledger ( ) . timestamp ( ) ;
30+ Self :: set_reputation ( env, & user, & reputation) ;
31+
32+ ParticipationUpdatedEvent {
33+ user : user. clone ( ) ,
34+ points_added : points,
35+ new_participation_score : reputation. participation_score ,
36+ updated_at : reputation. last_update ,
37+ }
38+ . publish ( env) ;
2339 }
24- . publish ( env) ;
25- }
2640
27- pub fn update_course_progress ( env : & Env , user : Address , is_completion : bool ) {
28- user. require_auth ( ) ;
29- let mut reputation = get_reputation ( env, & user) ;
30-
31- if is_completion {
32- reputation. total_courses_completed += 1 ;
33- // Logic: You can't complete a course without starting it,
34- // but simple increment here assumes course started logic handled elsewhere or previously
35- if reputation. total_courses_started < reputation. total_courses_completed {
36- reputation. total_courses_started = reputation. total_courses_completed ;
41+ /// Record a course start or completion for a user.
42+ pub fn update_course_progress ( env : & Env , user : Address , is_completion : bool ) {
43+ user. require_auth ( ) ;
44+ let mut reputation = Self :: get_reputation ( env, & user) ;
45+
46+ if is_completion {
47+ reputation. total_courses_completed += 1 ;
48+ if reputation. total_courses_started < reputation. total_courses_completed {
49+ reputation. total_courses_started = reputation. total_courses_completed ;
50+ }
51+ } else {
52+ reputation. total_courses_started += 1 ;
3753 }
38- } else {
39- reputation. total_courses_started += 1 ;
40- }
4154
42- if reputation. total_courses_started > 0 {
43- reputation. completion_rate =
44- ( reputation . total_courses_completed * BASIS_POINTS ) / reputation. total_courses_started ;
45- }
55+ if reputation. total_courses_started > 0 {
56+ reputation. completion_rate = ( reputation . total_courses_completed * BASIS_POINTS )
57+ / reputation. total_courses_started ;
58+ }
4659
47- reputation. last_update = env. ledger ( ) . timestamp ( ) ;
48- set_reputation ( env, & user, & reputation) ;
60+ reputation. last_update = env. ledger ( ) . timestamp ( ) ;
61+ Self :: set_reputation ( env, & user, & reputation) ;
4962
50- // Emit event
51- CourseProgressUpdatedEvent {
52- user : user. clone ( ) ,
53- total_courses_started : reputation. total_courses_started ,
54- total_courses_completed : reputation. total_courses_completed ,
55- completion_rate : reputation. completion_rate ,
56- updated_at : env. ledger ( ) . timestamp ( ) ,
63+ CourseProgressUpdatedEvent {
64+ user : user. clone ( ) ,
65+ total_courses_started : reputation. total_courses_started ,
66+ total_courses_completed : reputation. total_courses_completed ,
67+ completion_rate : reputation. completion_rate ,
68+ updated_at : reputation. last_update ,
69+ }
70+ . publish ( env) ;
5771 }
58- . publish ( env) ;
59- }
6072
61- pub fn rate_contribution ( env : & Env , user : Address , rating : u32 ) {
62- // Rating should be 0-5 scaled (e.g. 0-100 or 0-500)
63- // Here assuming 0-5
64- assert ! ( rating <= 5 , "Rating must be between 0 and 5" ) ;
65-
66- let mut reputation = get_reputation ( env, & user) ;
67-
68- let current_total_quality = reputation. contribution_quality * reputation. total_contributions ;
69- reputation. total_contributions += 1 ;
70-
71- // Weighted Average
72- reputation. contribution_quality =
73- ( current_total_quality + rating) / reputation. total_contributions ;
74- reputation. last_update = env. ledger ( ) . timestamp ( ) ;
75-
76- set_reputation ( env, & user, & reputation) ;
73+ /// Record a contribution rating (0–5) for a user.
74+ pub fn rate_contribution ( env : & Env , user : Address , rating : u32 ) {
75+ assert ! ( rating <= 5 , "Rating must be between 0 and 5" ) ;
76+
77+ let mut reputation = Self :: get_reputation ( env, & user) ;
78+ let current_total_quality = reputation. contribution_quality * reputation. total_contributions ;
79+ reputation. total_contributions += 1 ;
80+ reputation. contribution_quality =
81+ ( current_total_quality + rating) / reputation. total_contributions ;
82+ reputation. last_update = env. ledger ( ) . timestamp ( ) ;
83+ Self :: set_reputation ( env, & user, & reputation) ;
84+
85+ ContributionRatedEvent {
86+ user : user. clone ( ) ,
87+ rating,
88+ new_contribution_quality : reputation. contribution_quality ,
89+ total_contributions : reputation. total_contributions ,
90+ rated_at : reputation. last_update ,
91+ }
92+ . publish ( env) ;
93+ }
7794
78- // Emit event
79- ContributionRatedEvent {
80- user : user. clone ( ) ,
81- rating,
82- new_contribution_quality : reputation. contribution_quality ,
83- total_contributions : reputation. total_contributions ,
84- rated_at : env. ledger ( ) . timestamp ( ) ,
95+ // ===== Queries =====
96+
97+ /// Return the reputation record for a user, defaulting to zeroes.
98+ #[ must_use]
99+ pub fn get_reputation ( env : & Env , user : & Address ) -> UserReputation {
100+ env. storage ( )
101+ . persistent ( )
102+ . get ( & ( REPUTATION , user. clone ( ) ) )
103+ . unwrap_or ( UserReputation {
104+ participation_score : 0 ,
105+ completion_rate : 0 ,
106+ contribution_quality : 0 ,
107+ total_courses_started : 0 ,
108+ total_courses_completed : 0 ,
109+ total_contributions : 0 ,
110+ last_update : 0 ,
111+ } )
85112 }
86- . publish ( env) ;
87- }
88113
89- pub fn get_reputation ( env : & Env , user : & Address ) -> UserReputation {
90- env. storage ( )
91- . persistent ( )
92- . get ( & ( REPUTATION , user. clone ( ) ) )
93- . unwrap_or ( UserReputation {
94- participation_score : 0 ,
95- completion_rate : 0 ,
96- contribution_quality : 0 ,
97- total_courses_started : 0 ,
98- total_courses_completed : 0 ,
99- total_contributions : 0 ,
100- last_update : 0 ,
101- } )
102- }
114+ // ===== Internal =====
103115
104- fn set_reputation ( env : & Env , user : & Address , reputation : & UserReputation ) {
105- env. storage ( )
106- . persistent ( )
107- . set ( & ( REPUTATION , user. clone ( ) ) , reputation) ;
116+ fn set_reputation ( env : & Env , user : & Address , reputation : & UserReputation ) {
117+ env. storage ( )
118+ . persistent ( )
119+ . set ( & ( REPUTATION , user. clone ( ) ) , reputation) ;
120+ }
108121}
0 commit comments