use std::collections::BTreeMap;
use std::sync::{Arc, Mutex, RwLock};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Developer {
pub id: String,
pub name: String,
pub role: String,
pub join_date: String,
}
#[derive(Debug, Clone)]
pub struct ContributionMetrics {
pub commits_count: u32,
pub lines_added: u32,
pub lines_removed: u32,
pub bug_fixes: u32,
pub feature_implementations: u32,
pub code_review_count: u32,
pub documentation_improvements: u32,
pub tests_written: u32,
pub overall_score: f64, }
#[derive(Debug, Clone)]
pub struct ProductivityReport {
pub team_metrics: Vec<ContributionMetrics>,
pub team_velocity: f64,
pub average_contribution_score: f64,
pub top_performers: Vec<String>,
pub improvement_suggestions: Vec<String>,
pub team_strengths: Vec<String>,
pub team_weaknesses: Vec<String>,
}
pub struct ContributionTracker {
contribution_data: Arc<RwLock<HashMap<String, Vec<ContributionMetrics>>>,
developer_profiles: Arc<RwLock<HashMap<String, Developer>>>,
}
impl ContributionTracker {
pub fn new() -> Self {
Self {
contribution_data: Arc::new(Mutex::new(HashMap::new()))
developer_profiles: Arc::new(Mutex::new(HashMap::new()))
}
}
pub async fn calculate_contribution_metrics(
&self,
developer: &Developer,
timeframe: String,
) -> Result<ContributionMetrics, String> {
let mock_commits: _ = match developer.role.as_str() {
"Senior Engineer" => 150 + (rand::random::<u32>() % 100),
"Mid Engineer" => 100 + (rand::random::<u32>() % 100),
"Junior Engineer" => 50 + (rand::random::<u32>() % 50),
_ => 75,
};
let mock_lines_added: _ = mock_commits * 50 + (rand::random::<u32>() % 1000);
let mock_lines_removed: _ = mock_commits * 20 + (rand::random::<u32>() % 500);
let mock_bug_fixes: _ = match developer.role.as_str() {
"Senior Engineer" => 40 + (rand::random::<u32>() % 30),
"Mid Engineer" => 30 + (rand::random::<u32>() % 20),
"Junior Engineer" => 15 + (rand::random::<u32>() % 15),
_ => 25,
};
let mock_features: _ = match developer.role.as_str() {
"Senior Engineer" => 25 + (rand::random::<u32>() % 20),
"Mid Engineer" => 15 + (rand::random::<u32>() % 15),
"Junior Engineer" => 8 + (rand::random::<u32>() % 10),
_ => 12,
};
let mock_reviews: _ = match developer.role.as_str() {
"Senior Engineer" => 100 + (rand::random::<u32>() % 100),
"Mid Engineer" => 60 + (rand::random::<u32>() % 60),
"Junior Engineer" => 20 + (rand::random::<u32>() % 30),
_ => 50,
};
let mock_docs: _ = match developer.role.as_str() {
"Senior Engineer" => 30 + (rand::random::<u32>() % 20),
"Mid Engineer" => 20 + (rand::random::<u32>() % 15),
"Junior Engineer" => 10 + (rand::random::<u32>() % 10),
_ => 15,
};
let mock_tests: _ = match developer.role.as_str() {
"Senior Engineer" => 80 + (rand::random::<u32>() % 60),
"Mid Engineer" => 60 + (rand::random::<u32>() % 40),
"Junior Engineer" => 40 + (rand::random::<u32>() % 30),
_ => 50,
};
let overall_score: _ = self.calculate_overall_score(
mock_commits,
mock_lines_added,
mock_bug_fixes,
mock_features,
mock_reviews,
mock_docs,
mock_tests,
&developer.role,
);
let metrics: _ = ContributionMetrics {
commits_count: mock_commits,
lines_added: mock_lines_added,
lines_removed: mock_lines_removed,
bug_fixes: mock_bug_fixes,
feature_implementations: mock_features,
code_review_count: mock_reviews,
documentation_improvements: mock_docs,
tests_written: mock_tests,
overall_score,
};
let mut data = self.contribution_data.write().await;
data.entry(developer.id.clone())
.or_insert_with(Vec::new)
.push(metrics.clone());
let mut profiles = self.developer_profiles.write().await;
profiles.insert(developer.id.clone(), developer.clone());
Ok(metrics)
}
pub async fn generate_productivity_report(
&self,
team: &[Developer],
timeframe: String,
) -> Result<ProductivityReport, String> {
if team.is_empty() {
return Err("No team members provided".to_string());
}
let mut team_metrics = Vec::new();
let mut scores = Vec::new();
for developer in team {
match self.calculate_contribution_metrics(developer, timeframe.clone()).await {
Ok(metrics) => {
team_metrics.push(metrics.clone());
scores.push((developer.name.clone(), metrics.overall_score));
}
Err(e) => return Err(format!("Failed to calculate metrics for {}: {}", developer.name, e)),
}
}
let total_score: f64 = scores.iter().map(|(_, s)| s).sum();
let average_score: _ = total_score / scores.len() as f64;
let total_commits: u32 = team_metrics.iter().map(|m| m.commits_count).sum();
let total_features: u32 = team_metrics.iter().map(|m| m.feature_implementations).sum();
let team_velocity: _ = (total_commits as f64 * 0.4 + total_features as f64 * 0.6) / team.len() as f64;
scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
let top_performers: Vec<String> = scores.iter().take(3).map(|(name, _)| name.clone()).collect();
let improvement_suggestions: _ = self.generate_improvement_suggestions(&team_metrics);
let team_strengths: _ = self.identify_team_strengths(&team_metrics);
let team_weaknesses: _ = self.identify_team_weaknesses(&team_metrics);
Ok(ProductivityReport {
team_metrics,
team_velocity,
average_contribution_score: average_score,
top_performers,
improvement_suggestions,
team_strengths,
team_weaknesses,
})
}
fn calculate_overall_score(
&self,
commits: u32,
lines_added: u32,
bug_fixes: u32,
features: u32,
reviews: u32,
docs: u32,
tests: u32,
role: &str,
) -> f64 {
let (commit_weight, feature_weight, bug_weight, review_weight, doc_weight, test_weight) = match role {
"Senior Engineer" => (0.15, 0.25, 0.20, 0.15, 0.10, 0.15),
"Mid Engineer" => (0.20, 0.20, 0.15, 0.15, 0.10, 0.20),
"Junior Engineer" => (0.25, 0.15, 0.10, 0.10, 0.15, 0.25),
_ => (0.20, 0.20, 0.15, 0.15, 0.10, 0.20),
};
let normalized_commits: _ = (commits as f64 / 200.0 * 100.0).min(100.0);
let normalized_features: _ = (features as f64 / 40.0 * 100.0).min(100.0);
let normalized_bugs: _ = (bug_fixes as f64 / 60.0 * 100.0).min(100.0);
let normalized_reviews: _ = (reviews as f64 / 150.0 * 100.0).min(100.0);
let normalized_docs: _ = (docs as f64 / 40.0 * 100.0).min(100.0);
let normalized_tests: _ = (tests as f64 / 100.0 * 100.0).min(100.0);
let score: _ = normalized_commits * commit_weight
+ normalized_features * feature_weight
+ normalized_bugs * bug_weight
+ normalized_reviews * review_weight
+ normalized_docs * doc_weight
+ normalized_tests * test_weight;
score.max(0.0).min(100.0)
}
fn generate_improvement_suggestions(&self, metrics: &[ContributionMetrics]) -> Vec<String> {
let mut suggestions = Vec::new();
let avg_tests: f64 = metrics.iter().map(|m| m.tests_written).sum::<u32>() as f64 / metrics.len() as f64;
if avg_tests < 50.0 {
suggestions.push("建议提高测试覆盖率,编写更多单元测试和集成测试".to_string());
}
let avg_reviews: f64 = metrics.iter().map(|m| m.code_review_count).sum::<u32>() as f64 / metrics.len() as f64;
if avg_reviews < 30.0 {
suggestions.push("鼓励更多团队成员参与代码审查,提高代码质量".to_string());
}
let avg_docs: f64 = metrics.iter().map(|m| m.documentation_improvements).sum::<u32>() as f64 / metrics.len() as f64;
if avg_docs < 20.0 {
suggestions.push("改进文档质量,为代码添加更多注释和说明".to_string());
}
let avg_bugs: f64 = metrics.iter().map(|m| m.bug_fixes).sum::<u32>() as f64 / metrics.len() as f64;
if avg_bugs < 20.0 {
suggestions.push("在开发过程中更注重代码质量,减少 bug 的引入".to_string());
}
if suggestions.is_empty() {
suggestions.push("团队表现良好,保持当前的工作节奏".to_string());
}
suggestions
}
fn identify_team_strengths(&self, metrics: &[ContributionMetrics]) -> Vec<String> {
let mut strengths = Vec::new();
let total_commits: u32 = metrics.iter().map(|m| m.commits_count).sum();
let total_features: u32 = metrics.iter().map(|m| m.feature_implementations).sum();
let total_tests: u32 = metrics.iter().map(|m| m.tests_written).sum();
let total_reviews: u32 = metrics.iter().map(|m| m.code_review_count).sum();
if total_features > metrics.len() as u32 * 30 {
strengths.push("特性开发能力强".to_string());
}
if total_tests > metrics.len() as u32 * 80 {
strengths.push("测试覆盖率良好".to_string());
}
if total_reviews > metrics.len() as u32 * 80 {
strengths.push("代码审查文化积极".to_string());
}
let avg_score: f64 = metrics.iter().map(|m| m.overall_score).sum::<f64>() / metrics.len() as f64;
if avg_score > 80.0 {
strengths.push("整体开发效率高".to_string());
}
strengths
}
fn identify_team_weaknesses(&self, metrics: &[ContributionMetrics]) -> Vec<String> {
let mut weaknesses = Vec::new();
let avg_tests: f64 = metrics.iter().map(|m| m.tests_written).sum::<u32>() as f64 / metrics.len() as f64;
if avg_tests < 40.0 {
weaknesses.push("测试覆盖率有待提高".to_string());
}
let avg_docs: f64 = metrics.iter().map(|m| m.documentation_improvements).sum::<u32>() as f64 / metrics.len() as f64;
if avg_docs < 15.0 {
weaknesses.push("文档质量需要改进".to_string());
}
let avg_bugs: f64 = metrics.iter().map(|m| m.bug_fixes).sum::<u32>() as f64 / metrics.len() as f64;
if avg_bugs > 50.0 {
weaknesses.push("代码质量需要加强,bug 较多".to_string());
}
let avg_score: f64 = metrics.iter().map(|m| m.overall_score).sum::<f64>() / metrics.len() as f64;
if avg_score < 60.0 {
weaknesses.push("整体开发效率偏低".to_string());
}
weaknesses
}
pub async fn get_contribution_history(&self, developer_id: &str) -> Vec<ContributionMetrics> {
let data: _ = self.contribution_data.read().await;
data.get(developer_id).cloned().unwrap_or_default()
}
pub async fn get_team_ranking(&self, team_ids: &[String]) -> Vec<(String, f64)> {
let mut rankings = Vec::new();
for id in team_ids {
let history: _ = self.get_contribution_history(id).await;
if let Some(latest) = history.last() {
rankings.push((id.clone(), latest.overall_score));
}
}
rankings.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
rankings
}
}
impl Default for ContributionTracker {
fn default() -> Self {
Self::new()
}
}