use std::collections::HashMap;
use tracing::warn;
use crate::core::db::Database;
use crate::core::quality::QUALITY_FORMULA_VERSION;
use crate::report::errors::Result;
use crate::report::models::ReportData;
pub(super) fn parse_week_label_to_parts(label: &str) -> Option<(i64, i64)> {
let (y, w) = label.split_once("-W")?;
let year: i64 = y.parse().ok()?;
let week: i64 = w.parse().ok()?;
Some((year, week))
}
fn name_to_email_map(data: &ReportData) -> HashMap<String, String> {
data.authors
.iter()
.map(|a| (a.name.clone(), a.email.clone()))
.collect()
}
fn computed_at_secs() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0)
}
pub fn persist_weekly_quality(db: &Database, data: &ReportData) -> Result<usize> {
if data.weekly_activity.is_empty() {
return Ok(0);
}
let computed_at = computed_at_secs();
let name_to_email = name_to_email_map(data);
let rows: Vec<_> =
data.weekly_activity
.iter()
.filter_map(|wa| {
let (iso_year, iso_week) = match parse_week_label_to_parts(&wa.week) {
Some(p) => p,
None => {
warn!(
week = %wa.week,
"persist_weekly_quality: cannot parse week label; skipping row"
);
return None;
}
};
let quality_tshirt: i64 = wa.quality_tshirt.parse().unwrap_or(
crate::core::quality::size_for_quality_score(wa.quality_score) as i64,
);
Some((
wa.author.clone(),
iso_year,
iso_week,
wa.repository.clone(),
wa.quality_score,
quality_tshirt,
wa.revert_count as i64,
wa.bugfix_count as i64,
wa.ticketed_count as i64,
wa.commit_count as i64,
computed_at,
))
})
.collect();
let mut written = 0usize;
for chunk in rows.chunks(500) {
let conn = db.connection();
let tx = conn
.unchecked_transaction()
.map_err(crate::core::TgaError::from)?;
{
let mut stmt = tx
.prepare(
"INSERT OR REPLACE INTO fact_weekly_quality \
(author_email, iso_year, iso_week, repository, quality_score, \
quality_tshirt, revert_count, bugfix_count, ticketed_count, \
commit_count, formula_version, computed_at) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)",
)
.map_err(crate::core::TgaError::from)?;
for (author_display, iso_year, iso_week, repo, qs, qt, rc, bc, tc, cc, ca) in chunk {
let author_email = match name_to_email.get(author_display) {
Some(e) => e.clone(),
None => {
warn!(
author = %author_display,
iso_year = iso_year,
iso_week = iso_week,
"persist_weekly_quality: no email mapping for author; \
skipping row to avoid corrupting the grain key. \
Run `tga aliases list` to review unmapped identities."
);
continue;
}
};
stmt.execute(rusqlite::params![
author_email,
iso_year,
iso_week,
repo,
qs,
qt,
rc,
bc,
tc,
cc,
QUALITY_FORMULA_VERSION,
ca,
])
.map_err(crate::core::TgaError::from)?;
written += 1;
}
}
tx.commit().map_err(crate::core::TgaError::from)?;
}
Ok(written)
}
pub fn persist_weekly_engineer(db: &Database, data: &ReportData) -> Result<usize> {
if data.weekly_activity.is_empty() {
return Ok(0);
}
let computed_at = computed_at_secs();
let name_to_email = name_to_email_map(data);
let rows: Vec<_> = data
.weekly_activity
.iter()
.filter_map(|wa| {
let (iso_year, iso_week) = parse_week_label_to_parts(&wa.week)?;
let net = wa.commit_count.saturating_sub(wa.revert_count) as i64;
let agentic_pct = if net > 0 {
(wa.agentic_count as f64) / (net as f64) * 100.0
} else {
0.0
};
Some((
wa.author.clone(),
iso_year,
iso_week,
wa.repository.clone(),
net,
wa.agentic_count as i64,
wa.ide_assisted_count as i64,
agentic_pct,
computed_at,
))
})
.collect();
let mut written = 0usize;
for chunk in rows.chunks(500) {
let conn = db.connection();
let tx = conn
.unchecked_transaction()
.map_err(crate::core::TgaError::from)?;
{
let mut stmt = tx
.prepare(
"INSERT OR REPLACE INTO fact_weekly_engineer \
(author_email, iso_year, iso_week, repository, \
net_commits, agentic_count, ide_assisted_count, agentic_pct, \
formula_version, computed_at) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
)
.map_err(crate::core::TgaError::from)?;
for (author_display, iso_year, iso_week, repo, net, ac, ic, pct, ca) in chunk {
let author_email = match name_to_email.get(author_display) {
Some(e) => e.clone(),
None => {
warn!(
author = %author_display,
iso_year = iso_year,
iso_week = iso_week,
"persist_weekly_engineer: no email mapping for author; \
skipping row to avoid corrupting the grain key. \
Run `tga aliases list` to review unmapped identities."
);
continue;
}
};
stmt.execute(rusqlite::params![
author_email,
iso_year,
iso_week,
repo,
net,
ac,
ic,
pct,
"v1",
ca,
])
.map_err(crate::core::TgaError::from)?;
written += 1;
}
}
tx.commit().map_err(crate::core::TgaError::from)?;
}
Ok(written)
}