use crate::facts::FactsDb;
use crate::{CodeLoreError, Result};
pub fn enrich(db: &FactsDb, use_lineage: bool) -> Result<()> {
let src = if use_lineage {
crate::facts::ingest::materialize_changes_lineage(db)?;
"changes_lineage"
} else {
"changes"
};
enrich_diffusion(db)?;
enrich_size(db)?;
enrich_fix(db)?;
enrich_history(db, src)?;
enrich_experience(db, src)?;
Ok(())
}
fn enrich_diffusion(db: &FactsDb) -> Result<()> {
let sql_counts = "
UPDATE commits SET
nf = COALESCE(d.nf, 0),
ns = COALESCE(d.ns, 0),
nd = COALESCE(d.nd, 0)
FROM (
SELECT
rev,
COUNT(DISTINCT path) AS nf,
COUNT(DISTINCT SPLIT_PART(path, '/', 1)) AS ns,
COUNT(DISTINCT
CASE
WHEN STRPOS(path, '/') > 0
THEN SUBSTR(path, 1, LENGTH(path) - LENGTH(SPLIT_PART(path, '/', -1)) - 1)
ELSE ''
END
) AS nd
FROM changes
GROUP BY rev
) AS d
WHERE commits.rev = d.rev;
";
db.conn()
.execute_batch(sql_counts)
.map_err(|e| CodeLoreError::Analysis(format!("kamei diffusion counts: {e}")))?;
db.conn()
.execute_batch("UPDATE commits SET entropy = 0.0;")
.map_err(|e| CodeLoreError::Analysis(format!("kamei diffusion entropy reset: {e}")))?;
let sql_entropy = "
UPDATE commits SET entropy = e.h
FROM (
SELECT rev, -SUM(p * LOG2(p)) AS h
FROM (
SELECT
rev,
CAST(loc_added AS DOUBLE)
/ SUM(CAST(loc_added AS DOUBLE)) OVER (PARTITION BY rev) AS p
FROM changes
WHERE loc_added > 0
)
GROUP BY rev
) AS e
WHERE commits.rev = e.rev;
";
db.conn()
.execute_batch(sql_entropy)
.map_err(|e| CodeLoreError::Analysis(format!("kamei diffusion entropy: {e}")))?;
Ok(())
}
fn enrich_size(db: &FactsDb) -> Result<()> {
let sql_la_ld = "
UPDATE commits SET
la = COALESCE(s.la, 0),
ld = COALESCE(s.ld, 0)
FROM (
SELECT rev, SUM(loc_added) AS la, SUM(loc_deleted) AS ld
FROM changes
GROUP BY rev
) AS s
WHERE commits.rev = s.rev;
";
db.conn()
.execute_batch(sql_la_ld)
.map_err(|e| CodeLoreError::Analysis(format!("kamei size la/ld: {e}")))?;
db.conn()
.execute_batch("UPDATE commits SET lt = 0.0;")
.map_err(|e| CodeLoreError::Analysis(format!("kamei size lt: {e}")))?;
Ok(())
}
fn enrich_fix(db: &FactsDb) -> Result<()> {
let sql = "
UPDATE commits SET
fix = REGEXP_MATCHES(LOWER(message), '\\b(bug|fix|fixes|fixed|defect|patch|hotfix|issue|error)\\b');
";
db.conn()
.execute_batch(sql)
.map_err(|e| CodeLoreError::Analysis(format!("kamei fix: {e}")))?;
Ok(())
}
fn enrich_history(db: &FactsDb, src: &str) -> Result<()> {
db.conn()
.execute_batch("UPDATE commits SET ndev = 0, nuc = 0, age = 0.0;")
.map_err(|e| CodeLoreError::Analysis(format!("kamei history reset: {e}")))?;
let sql_history = format!(
"UPDATE commits SET
ndev = COALESCE(h.ndev, 0),
nuc = COALESCE(h.nuc, 0),
age = COALESCE(h.age, 0.0)
FROM (
WITH path_commit AS (
SELECT DISTINCT
ch.path,
cm.rev,
cm.canonical_author,
cm.date
FROM commits cm
INNER JOIN {src} ch ON ch.rev = cm.rev
),
per_path_last AS (
SELECT
curr.rev AS curr_rev,
curr.path AS curr_path,
curr.date AS curr_date,
MAX(prev.date) AS last_at
FROM path_commit curr
INNER JOIN path_commit prev
ON prev.path = curr.path
AND prev.date < curr.date
GROUP BY curr.rev, curr.path, curr.date
),
per_commit_age AS (
SELECT
curr_rev,
AVG(DATE_DIFF('day', last_at, curr_date)) AS age
FROM per_path_last
WHERE last_at IS NOT NULL
GROUP BY curr_rev
),
prior_pairs AS (
SELECT
curr.rev AS curr_rev,
prev.canonical_author AS prev_author,
prev.rev AS prev_rev
FROM path_commit curr
INNER JOIN path_commit prev
ON prev.path = curr.path
AND prev.date < curr.date
),
per_commit_counts AS (
SELECT
curr_rev,
COUNT(DISTINCT prev_author) AS ndev,
COUNT(DISTINCT prev_rev) AS nuc
FROM prior_pairs
GROUP BY curr_rev
)
SELECT
pcc.curr_rev,
pcc.ndev,
pcc.nuc,
pca.age
FROM per_commit_counts pcc
LEFT JOIN per_commit_age pca USING (curr_rev)
) AS h
WHERE commits.rev = h.curr_rev;"
);
db.conn()
.execute_batch(&sql_history)
.map_err(|e| CodeLoreError::Analysis(format!("kamei history ndev/nuc/age: {e}")))?;
Ok(())
}
fn enrich_experience(db: &FactsDb, src: &str) -> Result<()> {
db.conn()
.execute_batch("UPDATE commits SET exp = 0, rexp = 0.0, sexp = 0;")
.map_err(|e| CodeLoreError::Analysis(format!("kamei experience reset: {e}")))?;
let sql_exp_rexp = "
UPDATE commits SET exp = ae.exp, rexp = ae.rexp
FROM (
SELECT
c.rev AS curr_rev,
COUNT(*) AS exp,
SUM(1.0 / (1.0 + CAST(DATE_DIFF('year', prev.date, c.date) AS DOUBLE))) AS rexp
FROM commits c
INNER JOIN commits prev
ON prev.canonical_author = c.canonical_author
AND prev.date < c.date
GROUP BY c.rev
) AS ae
WHERE commits.rev = ae.curr_rev;
";
db.conn()
.execute_batch(sql_exp_rexp)
.map_err(|e| CodeLoreError::Analysis(format!("kamei exp/rexp: {e}")))?;
let sql_sexp = format!(
"UPDATE commits SET sexp = COALESCE(asx.sexp, 0)
FROM (
WITH distinct_dir_author_rev AS (
SELECT DISTINCT
SPLIT_PART(ch.path, '/', 1) AS dir,
cm.canonical_author,
cm.rev,
cm.date
FROM commits cm
INNER JOIN {src} ch ON ch.rev = cm.rev
),
ranked AS (
SELECT
rev,
ROW_NUMBER() OVER (
PARTITION BY dir, canonical_author
ORDER BY date, rev
) - 1 AS prior_count
FROM distinct_dir_author_rev
)
SELECT
rev AS curr_rev,
MAX(prior_count) AS sexp
FROM ranked
GROUP BY rev
) AS asx
WHERE commits.rev = asx.curr_rev;"
);
db.conn()
.execute_batch(&sql_sexp)
.map_err(|e| CodeLoreError::Analysis(format!("kamei sexp: {e}")))?;
Ok(())
}