Initial upload: GitPulse - Developer Productivity Analyzer CLI tool
This commit is contained in:
159
src/models/time_series.rs
Normal file
159
src/models/time_series.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub enum TimeBucket {
|
||||||
|
Hour,
|
||||||
|
Day,
|
||||||
|
Week,
|
||||||
|
Month,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeBucket {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
TimeBucket::Hour => "hour",
|
||||||
|
TimeBucket::Day => "day",
|
||||||
|
TimeBucket::Week => "week",
|
||||||
|
TimeBucket::Month => "month",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TimeSeriesData {
|
||||||
|
pub bucket: String,
|
||||||
|
pub bucket_type: String,
|
||||||
|
pub commits: usize,
|
||||||
|
pub lines_added: usize,
|
||||||
|
pub lines_removed: usize,
|
||||||
|
pub authors: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TimeSeriesData {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
bucket: String::new(),
|
||||||
|
bucket_type: String::new(),
|
||||||
|
commits: 0,
|
||||||
|
lines_added: 0,
|
||||||
|
lines_removed: 0,
|
||||||
|
authors: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct MovingAverage {
|
||||||
|
pub period: usize,
|
||||||
|
pub values: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MovingAverage {
|
||||||
|
pub fn new(period: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
period,
|
||||||
|
values: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate(&mut self, value: f64) -> f64 {
|
||||||
|
self.values.push(value);
|
||||||
|
if self.values.len() > self.period {
|
||||||
|
self.values.remove(0);
|
||||||
|
}
|
||||||
|
let sum: f64 = self.values.iter().sum();
|
||||||
|
sum / self.values.len() as f64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_ready(&self) -> bool {
|
||||||
|
self.values.len() >= self.period
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TrendAnalysis {
|
||||||
|
pub slope: f64,
|
||||||
|
pub direction: TrendDirection,
|
||||||
|
pub strength: f64,
|
||||||
|
pub prediction: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum TrendDirection {
|
||||||
|
Increasing,
|
||||||
|
Decreasing,
|
||||||
|
Stable,
|
||||||
|
Volatile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TrendAnalysis {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
slope: 0.0,
|
||||||
|
direction: TrendDirection::Stable,
|
||||||
|
strength: 0.0,
|
||||||
|
prediction: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_trend(values: &[f64]) -> TrendAnalysis {
|
||||||
|
if values.len() < 2 {
|
||||||
|
return TrendAnalysis::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = values.len() as f64;
|
||||||
|
let sum_x = (0..values.len()).map(|x| x as f64).sum::<f64>();
|
||||||
|
let sum_y = values.iter().sum::<f64>();
|
||||||
|
let sum_xy = values
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(x, y)| x as f64 * y)
|
||||||
|
.sum::<f64>();
|
||||||
|
let sum_x2 = values
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(x, _)| (x as f64).powi(2))
|
||||||
|
.sum::<f64>();
|
||||||
|
|
||||||
|
let slope = if n * sum_x2 - sum_x * sum_x != 0.0 {
|
||||||
|
(n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
let avg_y = sum_y / n;
|
||||||
|
let direction = if slope > 0.01 {
|
||||||
|
TrendDirection::Increasing
|
||||||
|
} else if slope < -0.01 {
|
||||||
|
TrendDirection::Decreasing
|
||||||
|
} else {
|
||||||
|
TrendDirection::Stable
|
||||||
|
};
|
||||||
|
|
||||||
|
let variance: f64 = values
|
||||||
|
.iter()
|
||||||
|
.map(|y| (y - avg_y).powi(2))
|
||||||
|
.sum::<f64>()
|
||||||
|
/ n;
|
||||||
|
let std_dev = variance.sqrt();
|
||||||
|
let strength = if std_dev > 0.0 {
|
||||||
|
(slope.abs() / std_dev).min(1.0)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
let prediction: Vec<f64> = (0..3)
|
||||||
|
.map(|i| {
|
||||||
|
let x = values.len() as f64 + i as f64;
|
||||||
|
slope * (x - sum_x / n) + avg_y
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
TrendAnalysis {
|
||||||
|
slope,
|
||||||
|
direction,
|
||||||
|
strength,
|
||||||
|
prediction,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user