← back to Handbag Auth Nextjs
scripts/generate-price-history.ts
145 lines
// Generate realistic historical price data for luxury handbags
// Based on actual market appreciation rates and trends
import Database from 'better-sqlite3'
import path from 'path'
const dbPath = path.join(process.cwd(), 'data', 'handbags.db')
const db = new Database(dbPath)
// Historical appreciation rates for luxury handbags (annual %)
const BRAND_APPRECIATION_RATES: Record<string, number> = {
'Hermès': 12, // Hermès Birkin/Kelly appreciate 12% annually
'Hermes': 12,
'HERMES': 12,
'Chanel': 10, // Chanel Classic Flap appreciates 10% annually
'CHANEL': 10,
'Louis Vuitton': 5, // LV appreciates slower
'LOUIS VUITTON': 5,
'Dior': 6,
'DIOR': 6,
'Gucci': 4,
'GUCCI': 4,
'Bottega Veneta': 5,
'Prada': 3,
'Fendi': 4,
'Celine': 5,
'Balenciaga': 3,
'Saint Laurent': 4,
'YSL': 4
}
function getAppreciationRate(brand: string): number {
for (const [key, rate] of Object.entries(BRAND_APPRECIATION_RATES)) {
if (brand.toUpperCase().includes(key.toUpperCase())) {
return rate
}
}
return 3 // Default 3% for other brands
}
function generatePriceHistory() {
console.log('Generating historical price data...\n')
// Get all active listings (no limit - process all)
const listings = db.prepare(`
SELECT id, brand, title, price_usd
FROM listings
WHERE is_active = 1 AND price_usd > 0
`).all() as any[]
console.log(`Found ${listings.length} active listings\n`)
// Clear existing price history
db.prepare('DELETE FROM price_history').run()
let totalInserted = 0
for (const listing of listings) {
const currentPrice = listing.price_usd
const appreciationRate = getAppreciationRate(listing.brand) / 100
// Generate monthly price points for past 5 years
const pricePoints: any[] = []
const now = new Date()
// Work backwards from current price
for (let monthsAgo = 60; monthsAgo >= 0; monthsAgo--) {
const date = new Date(now)
date.setMonth(date.getMonth() - monthsAgo)
// Calculate historical price (compound backwards)
const yearsAgo = monthsAgo / 12
const historicalPrice = currentPrice / Math.pow(1 + appreciationRate, yearsAgo)
// Add some realistic random variation (+/- 5%)
const variation = 1 + (Math.random() * 0.1 - 0.05)
const finalPrice = historicalPrice * variation
pricePoints.push({
listing_id: listing.id,
price_usd: Math.round(finalPrice * 100) / 100,
price_jpy: Math.round(finalPrice * 150 * 100) / 100, // ~150 JPY per USD
timestamp: date.toISOString(),
is_available: monthsAgo === 0 ? 1 : 0 // Only current price is available
})
}
// Insert price history
const insert = db.prepare(`
INSERT INTO price_history (listing_id, price_usd, price_jpy, timestamp, is_available)
VALUES (?, ?, ?, ?, ?)
`)
const insertMany = db.transaction((points: any[]) => {
for (const point of points) {
insert.run(
point.listing_id,
point.price_usd,
point.price_jpy,
point.timestamp,
point.is_available
)
}
})
insertMany(pricePoints)
totalInserted += pricePoints.length
if ((listings.indexOf(listing) + 1) % 100 === 0) {
console.log(`Processed ${listings.indexOf(listing) + 1} / ${listings.length} listings...`)
}
}
console.log(`\n✅ Generated ${totalInserted} price history records`)
console.log(`Average: ${Math.round(totalInserted / listings.length)} points per listing`)
// Show some statistics
const stats = db.prepare(`
SELECT
COUNT(DISTINCT listing_id) as listings_with_history,
COUNT(*) as total_points,
MIN(timestamp) as oldest_date,
MAX(timestamp) as newest_date
FROM price_history
`).get() as any
console.log('\nDatabase Statistics:')
console.log(`- Listings with history: ${stats.listings_with_history}`)
console.log(`- Total data points: ${stats.total_points}`)
console.log(`- Date range: ${stats.oldest_date.slice(0, 10)} to ${stats.newest_date.slice(0, 10)}`)
}
// Run if called directly
if (import.meta.url === `file://${process.argv[1]}`) {
try {
generatePriceHistory()
db.close()
} catch (error) {
console.error('Error:', error)
process.exit(1)
}
}
export { generatePriceHistory }