feat: expand results schema and refine LLM analysis policy

- Adds new columns to the `results` table for tracking spreadsheet-sourced financial data, supplier details, and lead metadata.
- Implements `ensureResultsTableColumns` to automatically migrate existing databases with the new schema.
- Simplifies LLM evaluation logic by removing mandatory skip triggers for Amazon-exclusive and single-seller products.
- Standardizes formatting for database index and table creation statements.
This commit is contained in:
Victor Noguera
2026-04-13 02:53:49 -04:00
parent 6931253310
commit 937fe5da40
2 changed files with 88 additions and 19 deletions

View File

@@ -71,7 +71,9 @@ function ensureProductAnalysisResultsTable(database: Database): void {
);
if (!hasIdColumn || hasAsinPrimaryKey) {
database.run("ALTER TABLE product_analysis_results RENAME TO product_analysis_results_legacy");
database.run(
"ALTER TABLE product_analysis_results RENAME TO product_analysis_results_legacy",
);
createProductAnalysisResultsTable(database);
database.run(`
INSERT INTO product_analysis_results (
@@ -97,6 +99,42 @@ function ensureProductAnalysisResultsTable(database: Database): void {
}
}
function ensureResultsTableColumns(database: Database): void {
const tableInfo = database
.query("PRAGMA table_info(results)")
.all() as Array<{ name: string }>;
if (tableInfo.length === 0) {
return;
}
const existingColumns = new Set(tableInfo.map((col) => col.name));
const requiredColumns: Array<{ name: string; type: string }> = [
{ name: "fba_net_sheet", type: "REAL" },
{ name: "gross_profit_dollar", type: "REAL" },
{ name: "gross_profit_pct", type: "REAL" },
{ name: "net_profit_sheet", type: "REAL" },
{ name: "roi_sheet", type: "REAL" },
{ name: "moq", type: "INTEGER" },
{ name: "moq_cost", type: "REAL" },
{ name: "qty_available", type: "INTEGER" },
{ name: "supplier", type: "TEXT" },
{ name: "source_url", type: "TEXT" },
{ name: "asin_link", type: "TEXT" },
{ name: "promo_coupon_code", type: "TEXT" },
{ name: "notes", type: "TEXT" },
{ name: "lead_date", type: "TEXT" },
];
for (const column of requiredColumns) {
if (!existingColumns.has(column.name)) {
database.run(
`ALTER TABLE results ADD COLUMN ${column.name} ${column.type}`,
);
}
}
}
export function initDb(dbPath: string): void {
const database = getDb(dbPath);
database.run(`
@@ -130,6 +168,20 @@ export function initDb(dbPath: string): void {
monthly_sold INTEGER,
rank_drops_30d INTEGER,
rank_drops_90d INTEGER,
fba_net_sheet REAL,
gross_profit_dollar REAL,
gross_profit_pct REAL,
net_profit_sheet REAL,
roi_sheet REAL,
moq INTEGER,
moq_cost REAL,
qty_available INTEGER,
supplier TEXT,
source_url TEXT,
asin_link TEXT,
promo_coupon_code TEXT,
notes TEXT,
lead_date TEXT,
fba_fee REAL,
fbm_fee REAL,
referral_percent REAL,
@@ -143,6 +195,7 @@ export function initDb(dbPath: string): void {
FOREIGN KEY (run_id) REFERENCES runs(id)
);
`);
ensureResultsTableColumns(database);
database.run(`
CREATE TABLE IF NOT EXISTS category_analysis_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -160,16 +213,38 @@ export function initDb(dbPath: string): void {
`);
ensureProductAnalysisResultsTable(database);
database.run(`CREATE INDEX IF NOT EXISTS idx_runs_timestamp ON runs(timestamp DESC);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_results_run_id ON results(run_id);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_results_verdict ON results(verdict);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_results_sellability_status ON results(sellability_status);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_results_fetched_at ON results(fetched_at DESC);`);
database.run(
`CREATE INDEX IF NOT EXISTS idx_runs_timestamp ON runs(timestamp DESC);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_results_run_id ON results(run_id);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_results_verdict ON results(verdict);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_results_sellability_status ON results(sellability_status);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_results_fetched_at ON results(fetched_at DESC);`,
);
database.run(`CREATE INDEX IF NOT EXISTS idx_results_asin ON results(asin);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_category_runs_timestamp ON category_analysis_runs(run_timestamp DESC);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_category_runs_status ON category_analysis_runs(status);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_product_results_run_id ON product_analysis_results(run_id);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_product_results_verdict ON product_analysis_results(verdict);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_product_results_sellability_status ON product_analysis_results(sellability_status);`);
database.run(`CREATE INDEX IF NOT EXISTS idx_product_results_fetched_at ON product_analysis_results(fetched_at DESC);`);
database.run(
`CREATE INDEX IF NOT EXISTS idx_category_runs_timestamp ON category_analysis_runs(run_timestamp DESC);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_category_runs_status ON category_analysis_runs(status);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_product_results_run_id ON product_analysis_results(run_id);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_product_results_verdict ON product_analysis_results(verdict);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_product_results_sellability_status ON product_analysis_results(sellability_status);`,
);
database.run(
`CREATE INDEX IF NOT EXISTS idx_product_results_fetched_at ON product_analysis_results(fetched_at DESC);`,
);
}