feat: initialize asin-check project with Bun
- Add README.md with installation and usage instructions. - Create bun.lock for dependency management. - Add package.json to define project metadata and dependencies. - Implement caching with Redis in cache.ts for ASIN data. - Configure environment variables in config.ts for API keys and Redis URL. - Develop main application logic in index.ts to read products, fetch data, and analyze results. - Integrate Keepa API for product data retrieval in keepa.ts. - Create LLM analysis functionality in llm.ts for product viability assessment. - Implement product reading from Excel files in reader.ts. - Stub SP-API integration in sp-api.ts for future implementation. - Define TypeScript types in types.ts for product and analysis data structures. - Write results to console and CSV in writer.ts. - Configure TypeScript settings in tsconfig.json for project compilation.
This commit is contained in:
67
src/writer.ts
Normal file
67
src/writer.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import * as XLSX from "xlsx";
|
||||
import type { AnalysisResult } from "./types.ts";
|
||||
|
||||
function buildRow(r: AnalysisResult) {
|
||||
const price = r.product.keepa?.currentPrice ?? r.product.spApi.estimatedSalePrice;
|
||||
const rank = r.product.keepa?.salesRank ?? r.product.record.amazonRank;
|
||||
|
||||
return {
|
||||
ASIN: r.product.record.asin,
|
||||
Name: r.product.record.name,
|
||||
Brand: r.product.record.brand ?? "",
|
||||
Category: r.product.record.category ?? r.product.keepa?.categoryTree?.join(" > ") ?? "",
|
||||
"Unit Cost": r.product.record.unitCost,
|
||||
"Current Price": price ?? "",
|
||||
"Avg Price 90d": r.product.keepa?.avgPrice90 ?? "",
|
||||
"Sales Rank": rank ?? "",
|
||||
"Rank Avg 90d": r.product.keepa?.salesRankAvg90 ?? "",
|
||||
Sellers: r.product.keepa?.sellerCount ?? "",
|
||||
"Monthly Sold": r.product.keepa?.monthlySold ?? "",
|
||||
"Rank Drops 30d": r.product.keepa?.salesRankDrops30 ?? "",
|
||||
"Rank Drops 90d": r.product.keepa?.salesRankDrops90 ?? "",
|
||||
"FBA Net (sheet)": r.product.record.fbaNet ?? "",
|
||||
"Gross Profit $": r.product.record.grossProfit ?? "",
|
||||
"Gross Profit %": r.product.record.grossProfitPct ?? "",
|
||||
MOQ: r.product.record.moq ?? "",
|
||||
"MOQ Cost": r.product.record.moqCost ?? "",
|
||||
"Qty Available": r.product.record.totalQtyAvail ?? "",
|
||||
"FBA Fee": r.product.spApi.fbaFee,
|
||||
"FBM Fee": r.product.spApi.fbmFee,
|
||||
"Referral %": r.product.spApi.referralFeePercent,
|
||||
Verdict: r.verdict.verdict,
|
||||
Confidence: r.verdict.confidence,
|
||||
Reasoning: r.verdict.reasoning,
|
||||
};
|
||||
}
|
||||
|
||||
export function printResults(results: AnalysisResult[]): void {
|
||||
const rows = results.map((r) => {
|
||||
const row = buildRow(r);
|
||||
return {
|
||||
...row,
|
||||
Name: row.Name.slice(0, 40),
|
||||
Category: String(row.Category).slice(0, 20),
|
||||
Reasoning: row.Reasoning.slice(0, 60),
|
||||
};
|
||||
});
|
||||
|
||||
console.log("\n=== Analysis Results ===\n");
|
||||
console.table(rows);
|
||||
|
||||
const summary = {
|
||||
FBA: results.filter((r) => r.verdict.verdict === "FBA").length,
|
||||
FBM: results.filter((r) => r.verdict.verdict === "FBM").length,
|
||||
SKIP: results.filter((r) => r.verdict.verdict === "SKIP").length,
|
||||
};
|
||||
console.log(`\nSummary: ${summary.FBA} FBA | ${summary.FBM} FBM | ${summary.SKIP} SKIP out of ${results.length} products\n`);
|
||||
}
|
||||
|
||||
export function writeResultsCsv(results: AnalysisResult[], outputPath: string): void {
|
||||
const rows = results.map(buildRow);
|
||||
|
||||
const ws = XLSX.utils.json_to_sheet(rows);
|
||||
const wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Results");
|
||||
XLSX.writeFile(wb, outputPath);
|
||||
console.log(`Results written to ${outputPath}`);
|
||||
}
|
||||
Reference in New Issue
Block a user