From 916f435dbc65f539effc19d38f54ff5accf1d0ca Mon Sep 17 00:00:00 2001 From: Mahmoud-Emad Date: Wed, 21 May 2025 15:04:45 +0300 Subject: [PATCH] feat: Load voting --- actix_mvc_app/src/controllers/governance.rs | 101 ++++++++++++++++++-- actix_mvc_app/src/db/proposals.rs | 22 ++++- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/actix_mvc_app/src/controllers/governance.rs b/actix_mvc_app/src/controllers/governance.rs index 14c1c6c..810e353 100644 --- a/actix_mvc_app/src/controllers/governance.rs +++ b/actix_mvc_app/src/controllers/governance.rs @@ -130,8 +130,8 @@ impl GovernanceController { if let Ok(Some(proposal)) = proposal { ctx.insert("proposal", &proposal); - // Get mock votes for this proposal - let votes = Self::get_mock_votes_for_proposal(&proposal_id); + // Extract votes directly from the proposal + let votes = Self::extract_votes_from_proposal(&proposal); ctx.insert("votes", &votes); // Calculate voting results directly from the proposal @@ -300,15 +300,15 @@ impl GovernanceController { proposal_id_u32, user_id, &form.vote_type, - 1, // Default to 1 share + 1, // Default to 1 share + form.comment.as_ref().map(|s| s.to_string()), // Pass the comment from the form ) { Ok(updated_proposal) => { ctx.insert("proposal", &updated_proposal); ctx.insert("success", "Your vote has been recorded!"); - // Get votes for this proposal - // For now, we'll still use mock votes until we implement a function to extract votes from the proposal - let votes = Self::get_mock_votes_for_proposal(&proposal_id); + // Extract votes directly from the updated proposal + let votes = Self::extract_votes_from_proposal(&updated_proposal); ctx.insert("votes", &votes); // Calculate voting results directly from the updated proposal @@ -639,6 +639,95 @@ impl GovernanceController { results } + /// Extract votes from a proposal's ballots + fn extract_votes_from_proposal(proposal: &Proposal) -> Vec { + let mut votes = Vec::new(); + + // Debug: Print proposal ID and number of ballots + println!( + "Extracting votes from proposal ID: {}", + proposal.base_data.id + ); + println!("Number of ballots in proposal: {}", proposal.ballots.len()); + + // If there are no ballots, create some mock votes for testing + if proposal.ballots.is_empty() { + println!("No ballots found in proposal, creating mock votes for testing"); + + // Create mock votes based on the option counts + for option in &proposal.options { + if option.count > 0 { + let vote_type = match option.id { + 1 => VoteType::Yes, + 2 => VoteType::No, + 3 => VoteType::Abstain, + _ => continue, + }; + + // Create a mock vote for each count + for i in 0..option.count { + let vote = Vote::new( + proposal.base_data.id.to_string(), + i as i32 + 1, + format!("User {}", i + 1), + vote_type.clone(), + option.comment.clone(), + ); + votes.push(vote); + } + } + } + + println!("Created {} mock votes", votes.len()); + return votes; + } + + // Convert each ballot to a Vote + for (i, ballot) in proposal.ballots.iter().enumerate() { + println!( + "Processing ballot {}: user_id={}, option_id={}, shares={}", + i, ballot.user_id, ballot.vote_option_id, ballot.shares_count + ); + + // Map option_id to VoteType + let vote_type = match ballot.vote_option_id { + 1 => VoteType::Yes, + 2 => VoteType::No, + 3 => VoteType::Abstain, + _ => { + println!( + "Unknown option_id: {}, defaulting to Abstain", + ballot.vote_option_id + ); + VoteType::Abstain // Default to Abstain for unknown options + } + }; + + // Convert user_id from u32 to i32 safely + let voter_id = match i32::try_from(ballot.user_id) { + Ok(id) => id, + Err(e) => { + println!("Failed to convert user_id {} to i32: {}", ballot.user_id, e); + continue; // Skip this ballot if conversion fails + } + }; + + // Create a Vote from the ballot + let vote = Vote::new( + proposal.base_data.id.to_string(), + voter_id, + format!("User {}", voter_id), + vote_type, + ballot.comment.clone(), // Use the comment from the ballot + ); + + votes.push(vote); + } + + println!("Extracted {} votes from proposal", votes.len()); + votes + } + /// Generate mock statistics for the governance dashboard fn get_mock_statistics() -> GovernanceStats { GovernanceStats { diff --git a/actix_mvc_app/src/db/proposals.rs b/actix_mvc_app/src/db/proposals.rs index edc490f..042a358 100644 --- a/actix_mvc_app/src/db/proposals.rs +++ b/actix_mvc_app/src/db/proposals.rs @@ -4,11 +4,11 @@ use chrono::{Duration, Utc}; use heromodels::db::hero::OurDB; use heromodels::{ db::{Collection, Db}, - models::governance::{Proposal, ProposalStatus, VoteEventStatus}, + models::governance::{Proposal, ProposalStatus}, }; /// The path to the database file. Change this as needed for your environment. -pub const DB_PATH: &str = "/tmp/ourdb_governance3"; +pub const DB_PATH: &str = "/tmp/ourdb_governance6"; /// Returns a shared OurDB instance for the given path. You can wrap this in Arc/Mutex for concurrent access if needed. pub fn get_db(db_path: &str) -> Result { @@ -97,6 +97,7 @@ pub fn submit_vote_on_proposal( user_id: i32, vote_type: &str, shares_count: u32, // Default to 1 if not specified + comment: Option, ) -> Result { // Get the proposal from the database let db = get_db(DB_PATH).map_err(|e| format!("DB error: {}", e))?; @@ -173,6 +174,23 @@ pub fn submit_vote_on_proposal( ballot_id, user_id, option_id, shares_count ); + // Create a new ballot and add it to the proposal's ballots + use heromodels::models::governance::Ballot; + + // Use the Ballot::new constructor which handles the BaseModelData creation + let mut ballot = Ballot::new( + Some(ballot_id), + user_id as u32, + option_id, + shares_count as i64, + ); + + // Set the comment if provided + ballot.comment = comment; + + // Add the ballot to the proposal's ballots + proposal.ballots.push(ballot); + // Update the proposal's updated_at timestamp proposal.updated_at = Utc::now();