feat: offline report from SQLite, no PostgreSQL needed

- Add render_offline_report.py — reads .sqlite directly
- Reports include: DB contents, TKG breakdown, density histogram,
  trace timeline, top identities, identity details card
- Supports --identity filter (like online mode)
- Add release visualize-offline <sqlite> [-i identity] [-o output]
- Works with exported .sqlite from export_sqlite.py
- Uses sqlite-vec vec0 tables for vector metadata
This commit is contained in:
Accusys
2026-05-13 03:10:59 +08:00
parent 007fe10c2e
commit bbf8e64752
2 changed files with 274 additions and 0 deletions

View File

@@ -62,6 +62,17 @@ enum Commands {
#[arg(short = 'i', long)]
identity: Option<i64>,
},
/// Generate offline report from .sqlite file (no PostgreSQL needed)
VisualizeOffline {
/// Path to .sqlite file
sqlite_path: String,
/// Output path
#[arg(short, long)]
output: Option<String>,
/// Filter by identity_id
#[arg(short = 'i', long)]
identity: Option<i64>,
},
}
/// Run psql command and return stdout
@@ -469,6 +480,29 @@ async fn cmd_package(db: &PostgresDb, uuid: &str) -> Result<()> {
Ok(())
}
fn cmd_visualize_offline(sqlite_path: &str, output: Option<&str>, identity: Option<i64>) -> Result<()> {
let outpath = match output {
Some(p) => p.to_string(),
None => sqlite_path.replace(".sqlite", "_report.html"),
};
let script = "/Users/accusys/momentry_core_0.1/scripts/render_offline_report.py";
let mut args: Vec<String> = vec![script.to_string(), sqlite_path.to_string(), outpath.clone()];
if let Some(id) = identity {
args.push("--identity".to_string());
args.push(id.to_string());
}
let output = Command::new("/opt/homebrew/bin/python3.11")
.args(&args)
.output()
.context("Offline report script failed")?;
if !output.status.success() {
anyhow::bail!("Offline report: {}", String::from_utf8_lossy(&output.stderr));
}
println!("{}", String::from_utf8_lossy(&output.stdout));
println!("\n Open: {}", outpath);
Ok(())
}
// ---- Visualize ----
fn cmd_visualize(uuid: &str, typ: &str, output: Option<&str>, identity: Option<i64>) -> Result<()> {
@@ -613,6 +647,7 @@ async fn main() -> Result<()> {
Commands::Package { uuid } => cmd_package(&db, &uuid).await?,
Commands::Stats => cmd_stats()?,
Commands::Visualize { uuid, typ, output, identity } => cmd_visualize(&uuid, &typ, output.as_deref(), identity)?,
Commands::VisualizeOffline { sqlite_path, output, identity } => cmd_visualize_offline(&sqlite_path, output.as_deref(), identity)?,
}
Ok(())
}