From ae1f13d69e68fbdba0d51279a6f81a8b1ae96439 Mon Sep 17 00:00:00 2001 From: mblanke Date: Tue, 13 Jan 2026 11:32:49 -0500 Subject: [PATCH] feat: add scripts for database inspection and migration - Implemented multiple scripts to check and inspect meal plans, scraped data, and accommodations for Munich and Riga. - Added a migration script to convert scraped data into the application's database format. - Introduced new database service methods for querying and updating travel rates. - Enhanced server configuration for serving static files in production. - Updated PostCSS configuration for consistency. --- database/travel_rates.db | Bin 102400 -> 221184 bytes openFlightsService.js | 23 +- postcss.config.js | 4 +- scripts/checkMealPlan.js | 12 + scripts/checkScrapedData.js | 39 +++ scripts/debugMunichPlan.js | 61 ++++ scripts/inspectFreshData.js | 38 +++ scripts/inspectMunichEntry.js | 16 + scripts/inspectMunichKeyCodes.js | 16 + scripts/inspectMunichRaw.js | 16 + scripts/inspectMunichRawTables.js | 14 + scripts/listMunichTypes.js | 14 + scripts/migrateFreshData.js | 391 +++++++++++++++++++++++ scripts/peekMunichKeys.js | 13 + scripts/peekMunichType.js | 12 + scripts/queryMunich.js | 16 + scripts/queryMunichRates.js | 16 + scripts/rawMunichString.js | 11 + scripts/updateRiga.js | 41 +++ server.js | 15 +- services/databaseService.js | 509 +++++++++++++++--------------- 21 files changed, 1012 insertions(+), 265 deletions(-) create mode 100644 scripts/checkMealPlan.js create mode 100644 scripts/checkScrapedData.js create mode 100644 scripts/debugMunichPlan.js create mode 100644 scripts/inspectFreshData.js create mode 100644 scripts/inspectMunichEntry.js create mode 100644 scripts/inspectMunichKeyCodes.js create mode 100644 scripts/inspectMunichRaw.js create mode 100644 scripts/inspectMunichRawTables.js create mode 100644 scripts/listMunichTypes.js create mode 100644 scripts/migrateFreshData.js create mode 100644 scripts/peekMunichKeys.js create mode 100644 scripts/peekMunichType.js create mode 100644 scripts/queryMunich.js create mode 100644 scripts/queryMunichRates.js create mode 100644 scripts/rawMunichString.js create mode 100644 scripts/updateRiga.js diff --git a/database/travel_rates.db b/database/travel_rates.db index 7ae8d8a5676403510566251430b87c8a8985de6b..b9c5a6c63fca8bfbc259325a985edeac077bad19 100644 GIT binary patch literal 221184 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 V)(TW1VhV3zB-40LzIx|){~utV(w+bS diff --git a/openFlightsService.js b/openFlightsService.js index 3154429..c9daba8 100644 --- a/openFlightsService.js +++ b/openFlightsService.js @@ -264,29 +264,30 @@ async function generateFlights(originCode, destCode, departureDate) { selectedAirlines.forEach((airlineCode, idx) => { // Calculate realistic flight duration based on stop pattern + // NO DIRECT FLIGHTS from North America to Eastern Europe - all require stops let stops, stopCodes; let totalDuration; if (idx === 0) { - // Direct flight (if available) - stops = 0; - stopCodes = []; - totalDuration = 11 + Math.random() * 3; // 11-14 hours - } else if (idx === 1) { - // 1 stop + // 1 stop via London stops = 1; stopCodes = ["LHR"]; totalDuration = 13 + Math.random() * 2; // 13-15 hours - } else if (idx === 2) { - // 1 stop different city + } else if (idx === 1) { + // 1 stop via Paris stops = 1; stopCodes = ["CDG"]; - totalDuration = 13 + Math.random() * 2; + totalDuration = 13 + Math.random() * 2; // 13-15 hours + } else if (idx === 2) { + // 1 stop via Frankfurt + stops = 1; + stopCodes = ["FRA"]; + totalDuration = 14 + Math.random() * 2; // 14-16 hours } else { // 2 stops stops = 2; - stopCodes = ["FRA", "VIE"]; - totalDuration = 15 + Math.random() * 3; // 15-18 hours + stopCodes = ["AMS", "WAW"]; // Amsterdam + Warsaw + totalDuration = 16 + Math.random() * 2; // 16-18 hours } // Generate realistic departure times (6am-10am) diff --git a/postcss.config.js b/postcss.config.js index a7f73a2..c2ddf74 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,5 +1,5 @@ export default { plugins: { - '@tailwindcss/postcss': {}, + "@tailwindcss/postcss": {}, }, -} +}; diff --git a/scripts/checkMealPlan.js b/scripts/checkMealPlan.js new file mode 100644 index 0000000..2130269 --- /dev/null +++ b/scripts/checkMealPlan.js @@ -0,0 +1,12 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "database", "travel_rates.db") +); +console.log( + db + .prepare( + "select city_name, meal_plan_type from travel_rates where lower(city_name)='munich'" + ) + .get() +); diff --git a/scripts/checkScrapedData.js b/scripts/checkScrapedData.js new file mode 100644 index 0000000..0581f90 --- /dev/null +++ b/scripts/checkScrapedData.js @@ -0,0 +1,39 @@ +const Database = require("better-sqlite3"); +const db = new Database("travel_rates.db"); + +// Check tables +const tables = db + .prepare("SELECT name FROM sqlite_master WHERE type='table'") + .all(); +console.log( + "Tables in database:", + tables.map((t) => t.name) +); + +// Check rate entries +const rateCount = db + .prepare("SELECT COUNT(*) as count FROM rate_entries") + .get(); +console.log("\nRate entries:", rateCount.count); + +// Check accommodations +const accommCount = db + .prepare("SELECT COUNT(*) as count FROM accommodations") + .get(); +console.log("Accommodations:", accommCount.count); + +// Check Latvia/Riga +const latvia = db + .prepare( + ` + SELECT city, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec + FROM accommodations + WHERE country = 'Latvia' +` + ) + .all(); + +console.log("\nLatvia accommodations:"); +console.log(JSON.stringify(latvia, null, 2)); + +db.close(); diff --git a/scripts/debugMunichPlan.js b/scripts/debugMunichPlan.js new file mode 100644 index 0000000..f5c3664 --- /dev/null +++ b/scripts/debugMunichPlan.js @@ -0,0 +1,61 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const sourceDb = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const typePriority = [ + "C-Day 1-30", + "C-Day 31-120", + "C-Day 121 +", + "P-Day 1-30", + "P-Day 31-120", + "P-Day 121 +", +]; +const rateEntries = sourceDb + .prepare( + ` + SELECT country, city, rate_type, rate_amount, currency, raw_json + FROM rate_entries + WHERE lower(city) = 'munich' +` + ) + .all(); +const cityRates = {}; +for (const row of rateEntries) { + const data = JSON.parse(row.raw_json); + const type = (data["Type of Accommodation"] || "").trim(); + const priority = typePriority.indexOf(type); + const key = `${row.country}_${row.city}`; + const existing = cityRates[key]; + const existingPriority = + existing && typeof existing.priority === "number" + ? existing.priority + : Infinity; + const isKnownPriority = priority !== -1; + const isHigherPriority = priority < existingPriority; + const shouldReplace = + !existing || + (isKnownPriority && isHigherPriority) || + existingPriority === Infinity; + if (!shouldReplace) continue; + cityRates[key] = { + country: row.country, + city: row.city, + currency: row.currency, + meal_plan_type: + type || + existing?.meal_plan_type || + (isKnownPriority ? typePriority[priority] : null), + priority: isKnownPriority ? priority : existingPriority, + breakfast: parseFloat(data.Breakfast) || existing?.breakfast || null, + lunch: parseFloat(data.Lunch || data["Lunch"]) || existing?.lunch || null, + dinner: parseFloat(data.Dinner) || existing?.dinner || null, + incidentals: + parseFloat(data["Incidental Amount"]) || existing?.incidentals || null, + meal_total: + parseFloat(data["Meal Total"] || data["Meal Totaa l"]) || + existing?.meal_total || + null, + }; +} +console.log(cityRates); diff --git a/scripts/inspectFreshData.js b/scripts/inspectFreshData.js new file mode 100644 index 0000000..1aa5c0f --- /dev/null +++ b/scripts/inspectFreshData.js @@ -0,0 +1,38 @@ +const Database = require("better-sqlite3"); +const sourceDb = new Database("data/travel_rates_scraped.sqlite3"); + +// Check schema +console.log("\n=== ACCOMMODATIONS SCHEMA ==="); +const accomSchema = sourceDb + .prepare( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='accommodations'" + ) + .get(); +console.log(accomSchema.sql); + +// Get Latvia data +console.log("\n=== LATVIA ACCOMMODATIONS ==="); +const latvia = sourceDb + .prepare( + ` + SELECT * FROM accommodations + WHERE source_url LIKE '%Latvia%' OR raw_json LIKE '%Latvia%' + LIMIT 5 +` + ) + .all(); +console.log(JSON.stringify(latvia, null, 2)); + +// Search for Riga specifically +console.log("\n=== RIGA SEARCH ==="); +const riga = sourceDb + .prepare( + ` + SELECT * FROM accommodations + WHERE city LIKE '%Riga%' OR raw_json LIKE '%Riga%' +` + ) + .all(); +console.log(JSON.stringify(riga, null, 2)); + +sourceDb.close(); diff --git a/scripts/inspectMunichEntry.js b/scripts/inspectMunichEntry.js new file mode 100644 index 0000000..b5e5bca --- /dev/null +++ b/scripts/inspectMunichEntry.js @@ -0,0 +1,16 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const row = db + .prepare( + ` + SELECT rate_type, rate_amount, currency, raw_json + FROM rate_entries + WHERE lower(city) = 'munich' AND rate_type = 'breakfast' + LIMIT 1 +` + ) + .get(); +console.log(row); diff --git a/scripts/inspectMunichKeyCodes.js b/scripts/inspectMunichKeyCodes.js new file mode 100644 index 0000000..acf446d --- /dev/null +++ b/scripts/inspectMunichKeyCodes.js @@ -0,0 +1,16 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const row = db + .prepare( + "select raw_json from rate_entries where lower(city)='munich' limit 1" + ) + .get(); +const data = JSON.parse(row.raw_json); +const keys = Object.keys(data); +for (const k of keys) { + const codes = Array.from(k).map((c) => c.charCodeAt(0)); + console.log(k, codes); +} diff --git a/scripts/inspectMunichRaw.js b/scripts/inspectMunichRaw.js new file mode 100644 index 0000000..11bec05 --- /dev/null +++ b/scripts/inspectMunichRaw.js @@ -0,0 +1,16 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const row = db + .prepare( + ` + SELECT country, city, table_name, raw_html, raw_json + FROM raw_tables + WHERE country = 'Germany' AND city = 'Munich' + LIMIT 1 +` + ) + .get(); +console.log(row); diff --git a/scripts/inspectMunichRawTables.js b/scripts/inspectMunichRawTables.js new file mode 100644 index 0000000..e60213d --- /dev/null +++ b/scripts/inspectMunichRawTables.js @@ -0,0 +1,14 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const row = db + .prepare( + "select title, data_json from raw_tables where data_json like '%Munich%' limit 1" + ) + .get(); +console.log(row?.title); +if (row?.data_json) { + console.log(row.data_json.slice(0, 2000)); +} diff --git a/scripts/listMunichTypes.js b/scripts/listMunichTypes.js new file mode 100644 index 0000000..5a9e6d0 --- /dev/null +++ b/scripts/listMunichTypes.js @@ -0,0 +1,14 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const rows = db + .prepare("select raw_json from rate_entries where lower(city)='munich'") + .all(); +const types = new Set(); +for (const r of rows) { + const data = JSON.parse(r.raw_json); + types.add(data["Type of Accommodation"]); +} +console.log(types); diff --git a/scripts/migrateFreshData.js b/scripts/migrateFreshData.js new file mode 100644 index 0000000..d6602a7 --- /dev/null +++ b/scripts/migrateFreshData.js @@ -0,0 +1,391 @@ +/** + * Migration script to properly convert freshly scraped data to Node.js app format + * Source: data/travel_rates_scraped.sqlite3 (Python scraper with raw_json) + * Target: database/travel_rates.db (Node.js app schema) + */ + +const Database = require("better-sqlite3"); +const path = require("path"); + +const SOURCE_DB = path.join( + __dirname, + "..", + "data", + "travel_rates_scraped.sqlite3" +); +const TARGET_DB = path.join(__dirname, "..", "database", "travel_rates.db"); + +console.log("šŸš€ Starting fresh data migration...\n"); + +// Open databases +const sourceDb = new Database(SOURCE_DB, { readonly: true }); +const targetDb = new Database(TARGET_DB); + +// Initialize target schema (drop and recreate to ensure clean state) +targetDb.exec(` +DROP TABLE IF EXISTS travel_rates; + +CREATE TABLE travel_rates ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + city_key TEXT UNIQUE NOT NULL, + city_name TEXT NOT NULL, + province TEXT, + country TEXT, + region TEXT, + currency TEXT NOT NULL DEFAULT 'USD', -- accommodation currency (foreign = USD) + meal_currency TEXT, -- per-diem currency + meal_plan_type TEXT, -- e.g., C-Day 1-30 + meal_total REAL, + jan_accommodation REAL, + feb_accommodation REAL, + mar_accommodation REAL, + apr_accommodation REAL, + may_accommodation REAL, + jun_accommodation REAL, + jul_accommodation REAL, + aug_accommodation REAL, + sep_accommodation REAL, + oct_accommodation REAL, + nov_accommodation REAL, + dec_accommodation REAL, + standard_accommodation REAL, + breakfast REAL, + lunch REAL, + dinner REAL, + incidentals REAL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +`); + +// Clear existing data (already done by DROP TABLE above) +console.log("šŸ“‹ Schema created with nullable meal fields"); + +let inserted = 0; +let errors = 0; + +// Migrate accommodations (international rates) +console.log("šŸ“„ Migrating international accommodations..."); +const accommodations = sourceDb + .prepare( + ` + SELECT city, raw_json FROM accommodations + WHERE raw_json IS NOT NULL +` + ) + .all(); + +const insertStmt = targetDb.prepare(` + INSERT OR REPLACE INTO travel_rates ( + city_key, city_name, country, region, currency, + jan_accommodation, feb_accommodation, mar_accommodation, apr_accommodation, + may_accommodation, jun_accommodation, jul_accommodation, aug_accommodation, + sep_accommodation, oct_accommodation, nov_accommodation, dec_accommodation, + standard_accommodation + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +`); + +for (const row of accommodations) { + try { + const data = JSON.parse(row.raw_json); + const country = data.Country || ""; + const city = data.City || row.city || ""; + + if (!city || !country) continue; + + const cityKey = `${country.toLowerCase().replace(/\s+/g, "_")}_${city + .toLowerCase() + .replace(/\s+/g, "_")}`; + + // Parse monthly rates + const jan = parseFloat(data["Jan."]) || null; + const feb = parseFloat(data["Feb."]) || null; + const mar = parseFloat(data["Mar."]) || null; + const apr = parseFloat(data["Apr."]) || null; + const may = parseFloat(data["May"]) || null; + const jun = parseFloat(data["June"]) || null; + const jul = parseFloat(data["July"]) || null; + const aug = parseFloat(data["Aug."]) || null; + const sep = parseFloat(data["Sept."]) || null; + const oct = parseFloat(data["Oct."]) || null; + const nov = parseFloat(data["Nov."]) || null; + const dec = parseFloat(data["Dec."]) || null; + + // Standard accommodation = average of all available monthly values + const months = [ + jan, + feb, + mar, + apr, + may, + jun, + jul, + aug, + sep, + oct, + nov, + dec, + ].filter((v) => typeof v === "number" && !Number.isNaN(v)); + const standard = + months.length > 0 + ? months.reduce((sum, v) => sum + v, 0) / months.length + : null; + + insertStmt.run( + cityKey, + city, + country, + "International", + "USD", // Foreign city limits are published in USD + jan, + feb, + mar, + apr, + may, + jun, + jul, + aug, + sep, + oct, + nov, + dec, + standard + ); + + inserted++; + + if (inserted % 100 === 0) { + console.log(` ... ${inserted} cities migrated`); + } + } catch (error) { + errors++; + console.error(` āš ļø Error migrating ${row.city}:`, error.message); + } +} + +// Migrate per-diem rates (meal and incidentals) +console.log("\nšŸ“„ Migrating per-diem rates..."); +const rateEntries = sourceDb + .prepare( + ` + SELECT country, city, rate_type, rate_amount, currency, raw_json + FROM rate_entries + WHERE source = 'international' + AND city IS NOT NULL + AND country IS NOT NULL + ORDER BY country, city +` + ) + .all(); + +// Group by city, selecting preferred meal plan type (C-Day 1-30 > C-Day 31-120 > C-Day 121+ > P-Day 1-30 > P-Day 31-120 > P-Day 121+) +const typePriority = [ + "C-Day 1-30", + "C-Day 31-120", + "C-Day 121 +", + "P-Day 1-30", + "P-Day 31-120", + "P-Day 121 +", +]; + +const stripWeirdSpaces = (value) => + typeof value === "string" + ? value + .replace(/[\u00ad\u200b\u200c\u200d]/g, "") // soft hyphen & zero-widths + .replace(/[\u2010\u2011\u2012\u2013]/g, "-") + : value; + +const normalizeType = (value) => { + if (!value) return ""; + return stripWeirdSpaces(value) + .replace(/\s+/g, " ") + .replace(/Day (\d+)\s*\+/i, "Day $1 +") + .trim(); +}; + +const normalizeRecord = (obj) => { + const normalized = {}; + for (const [key, val] of Object.entries(obj || {})) { + const cleanKey = stripWeirdSpaces(key) + .replace(/\s+/g, " ") + .trim() + .toLowerCase(); + normalized[cleanKey] = val; + } + return normalized; +}; + +const pickValue = (record, candidates) => { + for (const key of candidates) { + const value = record[key]; + if (value !== undefined) return value; + } + return undefined; +}; + +const cityRates = {}; +for (const row of rateEntries) { + let data; + try { + data = JSON.parse(row.raw_json); + } catch (e) { + continue; + } + + const normalized = normalizeRecord(data); + + const type = normalizeType(normalized["type of accommodation"] || ""); + const priority = typePriority.indexOf(type); + const key = `${row.country}_${row.city}`; + + const breakfastVal = pickValue(normalized, ["breakfast"]); + const lunchVal = pickValue(normalized, ["lunch"]); + const dinnerVal = pickValue(normalized, ["dinner"]); + const incidentalsVal = pickValue(normalized, ["incidental amount"]); + const mealTotalVal = pickValue(normalized, [ + "meal total", + "meal totall", + "meal totaa l", + ]); + + // Initialize city entry if needed + if (!cityRates[key]) { + cityRates[key] = { + country: row.country, + city: row.city, + currency: row.currency, + meal_plan_type: type, + priority, + breakfast: parseFloat(breakfastVal) || null, + lunch: parseFloat(lunchVal) || null, + dinner: parseFloat(dinnerVal) || null, + incidentals: parseFloat(incidentalsVal) || null, + meal_total: parseFloat(mealTotalVal) || null, + }; + } + + // If this type is higher priority than stored, replace + const existing = cityRates[key]; + const existingPriority = + existing && typeof existing.priority === "number" + ? existing.priority + : Infinity; + + // Determine if this record should replace the existing one + const isKnownPriority = priority !== -1; + const isHigherPriority = priority < existingPriority; + const shouldReplace = + !existing || + (isKnownPriority && isHigherPriority) || + existingPriority === Infinity; + + if (!shouldReplace) continue; + + cityRates[key] = { + country: row.country, + city: row.city, + currency: row.currency, + meal_plan_type: + type || + existing?.meal_plan_type || + (isKnownPriority && priority >= 0 ? typePriority[priority] : null), + priority: isKnownPriority ? priority : existingPriority, + breakfast: parseFloat(breakfastVal) || existing?.breakfast || null, + lunch: parseFloat(lunchVal) || existing?.lunch || null, + dinner: parseFloat(dinnerVal) || existing?.dinner || null, + incidentals: parseFloat(incidentalsVal) || existing?.incidentals || null, + meal_total: parseFloat(mealTotalVal) || existing?.meal_total || null, + }; +} + +// Update existing cities with meal rates +const updateStmt = targetDb.prepare(` + UPDATE travel_rates + SET breakfast = ?, lunch = ?, dinner = ?, incidentals = ?, + meal_currency = COALESCE(meal_currency, ?), + meal_plan_type = COALESCE(?, meal_plan_type), + meal_total = COALESCE(?, meal_total) + WHERE city_key = ? +`); + +let updated = 0; +for (const [key, rates] of Object.entries(cityRates)) { + const cityKey = `${rates.country + .toLowerCase() + .replace(/\s+/g, "_")}_${rates.city.toLowerCase().replace(/\s+/g, "_")}`; + + const result = updateStmt.run( + rates.breakfast, + rates.lunch, + rates.dinner, + rates.incidentals, + rates.currency, + rates.meal_plan_type || + (typeof rates.priority === "number" && rates.priority >= 0 + ? typePriority[rates.priority] + : null), + rates.meal_total, + cityKey + ); + + if (result.changes > 0) { + updated++; + } +} + +// Final statistics +console.log("\nāœ… Migration complete!"); +console.log(` šŸ“Š Accommodations inserted: ${inserted}`); +console.log(` šŸ“Š Per-diem rates updated: ${updated}`); +console.log(` āš ļø Errors: ${errors}`); + +// Verify Riga +console.log("\nšŸ” Verifying Riga data:"); +const riga = targetDb + .prepare( + ` + SELECT city_name, country, currency, + jan_accommodation, feb_accommodation, standard_accommodation, + breakfast, lunch, dinner, incidentals, meal_currency, meal_plan_type + FROM travel_rates + WHERE LOWER(city_name) = 'riga' +` + ) + .get(); + +if (riga) { + console.log(" āœ… Riga found:"); + console.log(` Country: ${riga.country}`); + console.log(` Accommodation currency: ${riga.currency}`); + console.log( + ` Accommodation (Jan): ${riga.currency} $${riga.jan_accommodation}` + ); + console.log( + ` Accommodation (Standard): ${riga.currency} $${riga.standard_accommodation}` + ); + if (riga.meal_currency) + console.log(` Meal currency: ${riga.meal_currency}`); + if (riga.meal_plan_type) + console.log(` Meal plan: ${riga.meal_plan_type}`); + if (riga.breakfast) + console.log( + ` Breakfast: ${riga.meal_currency || ""} $${riga.breakfast}` + ); + if (riga.lunch) + console.log(` Lunch: ${riga.meal_currency || ""} $${riga.lunch}`); + if (riga.dinner) + console.log(` Dinner: ${riga.meal_currency || ""} $${riga.dinner}`); +} else { + console.log(" āš ļø Riga NOT found in database!"); +} + +// Show total count +const total = targetDb + .prepare("SELECT COUNT(*) as count FROM travel_rates") + .get(); +console.log(`\nšŸ“Š Total cities in database: ${total.count}`); + +sourceDb.close(); +targetDb.close(); + +console.log("\nāœ… Done!"); diff --git a/scripts/peekMunichKeys.js b/scripts/peekMunichKeys.js new file mode 100644 index 0000000..444e0a8 --- /dev/null +++ b/scripts/peekMunichKeys.js @@ -0,0 +1,13 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const row = db + .prepare( + "select raw_json from rate_entries where lower(city)='munich' and rate_type='breakfast' limit 1" + ) + .get(); +const data = JSON.parse(row.raw_json); +console.log(Object.keys(data)); +console.log(data); diff --git a/scripts/peekMunichType.js b/scripts/peekMunichType.js new file mode 100644 index 0000000..398672e --- /dev/null +++ b/scripts/peekMunichType.js @@ -0,0 +1,12 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const row = db + .prepare( + "select raw_json from rate_entries where lower(city)='munich' limit 1" + ) + .get(); +const data = JSON.parse(row.raw_json); +console.log(data["Type of Accommodation"]); diff --git a/scripts/queryMunich.js b/scripts/queryMunich.js new file mode 100644 index 0000000..b872437 --- /dev/null +++ b/scripts/queryMunich.js @@ -0,0 +1,16 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "database", "travel_rates.db") +); +const row = db + .prepare( + ` + SELECT city_name, country, currency, meal_currency, meal_plan_type, meal_total, + breakfast, lunch, dinner, incidentals + FROM travel_rates + WHERE lower(city_name) = 'munich' +` + ) + .get(); +console.log(row); diff --git a/scripts/queryMunichRates.js b/scripts/queryMunichRates.js new file mode 100644 index 0000000..9546162 --- /dev/null +++ b/scripts/queryMunichRates.js @@ -0,0 +1,16 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const rows = db + .prepare( + ` + SELECT country, city, rate_type, rate_amount, currency, source + FROM rate_entries + WHERE lower(city) = 'munich' + ORDER BY rate_type +` + ) + .all(); +console.log(rows); diff --git a/scripts/rawMunichString.js b/scripts/rawMunichString.js new file mode 100644 index 0000000..50911e8 --- /dev/null +++ b/scripts/rawMunichString.js @@ -0,0 +1,11 @@ +const Database = require("better-sqlite3"); +const path = require("path"); +const db = new Database( + path.join(__dirname, "..", "data", "travel_rates_scraped.sqlite3") +); +const row = db + .prepare( + "select raw_json from rate_entries where lower(city)='munich' limit 1" + ) + .get(); +console.log(row.raw_json); diff --git a/scripts/updateRiga.js b/scripts/updateRiga.js new file mode 100644 index 0000000..2a46584 --- /dev/null +++ b/scripts/updateRiga.js @@ -0,0 +1,41 @@ +const db = require("../services/databaseService"); + +async function updateRigaRate() { + await db.connect(); + + return new Promise((resolve, reject) => { + db.db.run( + ` + UPDATE travel_rates + SET jan_accommodation = 209, + feb_accommodation = 209, + mar_accommodation = 209, + apr_accommodation = 209, + may_accommodation = 209, + jun_accommodation = 209, + jul_accommodation = 209, + aug_accommodation = 209, + sep_accommodation = 209, + oct_accommodation = 209, + nov_accommodation = 209, + dec_accommodation = 209, + standard_accommodation = 209, + currency = 'CAD' + WHERE LOWER(city_name) = 'riga' + `, + [], + (err) => { + if (err) { + console.error("āŒ Error:", err); + reject(err); + } else { + console.log("āœ… Riga accommodation updated to CAD $209"); + resolve(); + } + db.close(); + } + ); + }); +} + +updateRigaRate().catch(console.error); diff --git a/server.js b/server.js index 8229a3b..aa54b57 100644 --- a/server.js +++ b/server.js @@ -81,12 +81,17 @@ app.use((req, res, next) => { }); // Serve React app (production build) or legacy static files -if (process.env.NODE_ENV === 'production' && require('fs').existsSync(path.join(__dirname, 'dist', 'client'))) { +if ( + process.env.NODE_ENV === "production" && + require("fs").existsSync(path.join(__dirname, "dist", "client")) +) { // Serve React production build - app.use(express.static(path.join(__dirname, 'dist', 'client'), { - maxAge: '1d', - etag: true, - })); + app.use( + express.static(path.join(__dirname, "dist", "client"), { + maxAge: "1d", + etag: true, + }) + ); } else { // Serve legacy static files from the current directory app.use( diff --git a/services/databaseService.js b/services/databaseService.js index 3f4246c..1c0b584 100644 --- a/services/databaseService.js +++ b/services/databaseService.js @@ -1,32 +1,32 @@ -const sqlite3 = require('sqlite3').verbose(); -const path = require('path'); +const sqlite3 = require("sqlite3").verbose(); +const path = require("path"); class DatabaseService { - constructor() { - this.dbPath = path.join(__dirname, '..', 'database', 'travel_rates.db'); - this.db = null; - } + constructor() { + this.dbPath = path.join(__dirname, "..", "database", "travel_rates.db"); + this.db = null; + } - connect() { - return new Promise((resolve, reject) => { - this.db = new sqlite3.Database(this.dbPath, (err) => { - if (err) { - console.error('āŒ Database connection failed:', err); - reject(err); - } else { - console.log('āœ… Database connected'); - resolve(); - } - }); - }); - } + connect() { + return new Promise((resolve, reject) => { + this.db = new sqlite3.Database(this.dbPath, (err) => { + if (err) { + console.error("āŒ Database connection failed:", err); + reject(err); + } else { + console.log("āœ… Database connected"); + resolve(); + } + }); + }); + } - /** - * Search for a city (complete travel rates) - * GUARANTEED to find Canberra! - */ - async searchCity(searchTerm) { - const query = ` + /** + * Search for a city (complete travel rates) + * GUARANTEED to find Canberra! + */ + async searchCity(searchTerm) { + const query = ` SELECT * FROM travel_rates WHERE LOWER(city_name) LIKE LOWER(?) OR LOWER(city_key) LIKE LOWER(?) @@ -42,54 +42,59 @@ class DatabaseService { LIMIT 10 `; - const term = `%${searchTerm}%`; - const exactTerm = searchTerm.toLowerCase(); - const likeTerm = `${searchTerm.toLowerCase()}%`; + const term = `%${searchTerm}%`; + const exactTerm = searchTerm.toLowerCase(); + const likeTerm = `${searchTerm.toLowerCase()}%`; - return new Promise((resolve, reject) => { - this.db.all(query, [term, term, term, term, exactTerm, exactTerm, likeTerm], (err, rows) => { - if (err) reject(err); - else resolve(rows ? rows.map(row => this.formatTravelRate(row)) : []); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all( + query, + [term, term, term, term, exactTerm, exactTerm, likeTerm], + (err, rows) => { + if (err) reject(err); + else + resolve(rows ? rows.map((row) => this.formatTravelRate(row)) : []); + } + ); + }); + } - /** - * Get complete travel rate by exact city key - */ - async getAccommodationRate(cityKey) { - const query = `SELECT * FROM travel_rates WHERE LOWER(city_key) = LOWER(?) LIMIT 1`; + /** + * Get complete travel rate by exact city key + */ + async getAccommodationRate(cityKey) { + const query = `SELECT * FROM travel_rates WHERE LOWER(city_key) = LOWER(?) LIMIT 1`; - return new Promise((resolve, reject) => { - this.db.get(query, [cityKey], (err, row) => { - if (err) reject(err); - else resolve(row ? this.formatTravelRate(row) : null); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.get(query, [cityKey], (err, row) => { + if (err) reject(err); + else resolve(row ? this.formatTravelRate(row) : null); + }); + }); + } - /** - * Get accommodation rate for a specific month - */ - async getMonthlyRate(cityKey, month) { - const rate = await this.getAccommodationRate(cityKey); - - if (!rate) return null; + /** + * Get accommodation rate for a specific month + */ + async getMonthlyRate(cityKey, month) { + const rate = await this.getAccommodationRate(cityKey); - const monthIndex = month - 1; // 0-based index - return { - city: rate.name, - month: month, - rate: rate.monthlyRates[monthIndex], - currency: rate.currency - }; - } + if (!rate) return null; - /** - * Full-text search across all cities - */ - async fullTextSearch(searchTerm) { - const query = ` + const monthIndex = month - 1; // 0-based index + return { + city: rate.name, + month: month, + rate: rate.monthlyRates[monthIndex], + currency: rate.currency, + }; + } + + /** + * Full-text search across all cities + */ + async fullTextSearch(searchTerm) { + const query = ` SELECT a.* FROM travel_rates a WHERE a.id IN ( SELECT rowid FROM travel_search @@ -103,147 +108,157 @@ class DatabaseService { LIMIT 20 `; - return new Promise((resolve, reject) => { - this.db.all(query, [searchTerm, searchTerm], (err, rows) => { - if (err) reject(err); - else resolve(rows.map(row => this.formatTravelRate(row))); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all(query, [searchTerm, searchTerm], (err, rows) => { + if (err) reject(err); + else resolve(rows.map((row) => this.formatTravelRate(row))); + }); + }); + } - /** - * Format complete travel rate for API response - */ - formatTravelRate(row) { - return { - cityKey: row.city_key, - name: row.city_name, - province: row.province, - country: row.country, - region: row.region, - currency: row.currency, - accommodation: { - monthly: [ - row.jan_accommodation, row.feb_accommodation, row.mar_accommodation, - row.apr_accommodation, row.may_accommodation, row.jun_accommodation, - row.jul_accommodation, row.aug_accommodation, row.sep_accommodation, - row.oct_accommodation, row.nov_accommodation, row.dec_accommodation - ], - standard: row.standard_accommodation - }, - meals: { - breakfast: row.breakfast, - lunch: row.lunch, - dinner: row.dinner, - total: row.total_meals - }, - incidentals: row.incidentals, - totalDailyAllowance: row.total_daily_allowance, - fullDayCost: parseFloat(row.standard_accommodation || row.jan_accommodation) + parseFloat(row.total_daily_allowance), - isInternational: row.is_international === 1 - }; - } + /** + * Format complete travel rate for API response + */ + formatTravelRate(row) { + return { + cityKey: row.city_key, + name: row.city_name, + province: row.province, + country: row.country, + region: row.region, + currency: row.currency, + accommodation: { + monthly: [ + row.jan_accommodation, + row.feb_accommodation, + row.mar_accommodation, + row.apr_accommodation, + row.may_accommodation, + row.jun_accommodation, + row.jul_accommodation, + row.aug_accommodation, + row.sep_accommodation, + row.oct_accommodation, + row.nov_accommodation, + row.dec_accommodation, + ], + standard: row.standard_accommodation, + }, + meals: { + breakfast: row.breakfast, + lunch: row.lunch, + dinner: row.dinner, + total: row.total_meals, + }, + incidentals: row.incidentals, + totalDailyAllowance: row.total_daily_allowance, + fullDayCost: + parseFloat(row.standard_accommodation || row.jan_accommodation) + + parseFloat(row.total_daily_allowance), + isInternational: row.is_international === 1, + }; + } - /** - * Legacy format for backward compatibility - */ - formatAccommodationRate(row) { - return { - cityKey: row.city_key, - name: row.city_name, - province: row.province, - country: row.country, - region: row.region, - currency: row.currency, - monthlyRates: [ - row.jan_accommodation || row.jan_rate, - row.feb_accommodation || row.feb_rate, - row.mar_accommodation || row.mar_rate, - row.apr_accommodation || row.apr_rate, - row.may_accommodation || row.may_rate, - row.jun_accommodation || row.jun_rate, - row.jul_accommodation || row.jul_rate, - row.aug_accommodation || row.aug_rate, - row.sep_accommodation || row.sep_rate, - row.oct_accommodation || row.oct_rate, - row.nov_accommodation || row.nov_rate, - row.dec_accommodation || row.dec_rate - ], - standardRate: row.standard_accommodation || row.standard_rate, - isInternational: row.is_international === 1, - effectiveDate: row.effective_date - }; - } + /** + * Legacy format for backward compatibility + */ + formatAccommodationRate(row) { + return { + cityKey: row.city_key, + name: row.city_name, + province: row.province, + country: row.country, + region: row.region, + currency: row.currency, + monthlyRates: [ + row.jan_accommodation || row.jan_rate, + row.feb_accommodation || row.feb_rate, + row.mar_accommodation || row.mar_rate, + row.apr_accommodation || row.apr_rate, + row.may_accommodation || row.may_rate, + row.jun_accommodation || row.jun_rate, + row.jul_accommodation || row.jul_rate, + row.aug_accommodation || row.aug_rate, + row.sep_accommodation || row.sep_rate, + row.oct_accommodation || row.oct_rate, + row.nov_accommodation || row.nov_rate, + row.dec_accommodation || row.dec_rate, + ], + standardRate: row.standard_accommodation || row.standard_rate, + isInternational: row.is_international === 1, + effectiveDate: row.effective_date, + }; + } - /** - * List all cities by region - */ - async getCitiesByRegion(region) { - const query = ` + /** + * List all cities by region + */ + async getCitiesByRegion(region) { + const query = ` SELECT * FROM travel_rates WHERE region = ? ORDER BY city_name `; - return new Promise((resolve, reject) => { - this.db.all(query, [region], (err, rows) => { - if (err) reject(err); - else resolve(rows.map(row => this.formatAccommodationRate(row))); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all(query, [region], (err, rows) => { + if (err) reject(err); + else resolve(rows.map((row) => this.formatAccommodationRate(row))); + }); + }); + } - /** - * List all cities by country - */ - async getCitiesByCountry(country) { - const query = ` + /** + * List all cities by country + */ + async getCitiesByCountry(country) { + const query = ` SELECT * FROM travel_rates WHERE LOWER(country) = LOWER(?) ORDER BY city_name `; - return new Promise((resolve, reject) => { - this.db.all(query, [country], (err, rows) => { - if (err) reject(err); - else resolve(rows.map(row => this.formatAccommodationRate(row))); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all(query, [country], (err, rows) => { + if (err) reject(err); + else resolve(rows.map((row) => this.formatAccommodationRate(row))); + }); + }); + } - /** - * Get all available regions - */ - async getAllRegions() { - const query = `SELECT DISTINCT region FROM travel_rates ORDER BY region`; + /** + * Get all available regions + */ + async getAllRegions() { + const query = `SELECT DISTINCT region FROM travel_rates ORDER BY region`; - return new Promise((resolve, reject) => { - this.db.all(query, [], (err, rows) => { - if (err) reject(err); - else resolve(rows.map(row => row.region)); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all(query, [], (err, rows) => { + if (err) reject(err); + else resolve(rows.map((row) => row.region)); + }); + }); + } - /** - * Get all available countries - */ - async getAllCountries() { - const query = `SELECT DISTINCT country FROM travel_rates ORDER BY country`; + /** + * Get all available countries + */ + async getAllCountries() { + const query = `SELECT DISTINCT country FROM travel_rates ORDER BY country`; - return new Promise((resolve, reject) => { - this.db.all(query, [], (err, rows) => { - if (err) reject(err); - else resolve(rows.map(row => row.country)); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all(query, [], (err, rows) => { + if (err) reject(err); + else resolve(rows.map((row) => row.country)); + }); + }); + } - /** - * Autocomplete for city search - */ - async autocomplete(prefix, limit = 10) { - const query = ` + /** + * Autocomplete for city search + */ + async autocomplete(prefix, limit = 10) { + const query = ` SELECT city_name, country, region FROM travel_rates WHERE LOWER(city_name) LIKE LOWER(?) ORDER BY @@ -255,22 +270,22 @@ class DatabaseService { LIMIT ? `; - const term = `${prefix}%`; - const exactTerm = `${prefix}`; + const term = `${prefix}%`; + const exactTerm = `${prefix}`; - return new Promise((resolve, reject) => { - this.db.all(query, [term, exactTerm, limit], (err, rows) => { - if (err) reject(err); - else resolve(rows); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all(query, [term, exactTerm, limit], (err, rows) => { + if (err) reject(err); + else resolve(rows); + }); + }); + } - /** - * Get all per-diem rates - */ - async getAllPerDiemRates() { - const query = ` + /** + * Get all per-diem rates + */ + async getAllPerDiemRates() { + const query = ` SELECT country, city_name as city, breakfast, lunch, dinner, incidentals, currency FROM travel_rates @@ -279,19 +294,19 @@ class DatabaseService { ORDER BY country, city_name `; - return new Promise((resolve, reject) => { - this.db.all(query, [], (err, rows) => { - if (err) reject(err); - else resolve(rows); - }); - }); - } + return new Promise((resolve, reject) => { + this.db.all(query, [], (err, rows) => { + if (err) reject(err); + else resolve(rows); + }); + }); + } - /** - * Get all accommodation rates - */ - async getAllAccommodations() { - const query = ` + /** + * Get all accommodation rates + */ + async getAllAccommodations() { + const query = ` SELECT city_name as city, province, accommodation_rate as rate, currency FROM travel_rates WHERE country = 'Canada' @@ -299,42 +314,42 @@ class DatabaseService { ORDER BY province, city_name `; - return new Promise((resolve, reject) => { - this.db.all(query, [], (err, rows) => { - if (err) reject(err); - else resolve(rows); - }); + return new Promise((resolve, reject) => { + this.db.all(query, [], (err, rows) => { + if (err) reject(err); + else resolve(rows); + }); + }); + } + + /** + * Get statistics + */ + async getStats() { + const queries = { + countries: `SELECT COUNT(DISTINCT country) as count FROM travel_rates WHERE country != 'Canada'`, + accommodations: `SELECT COUNT(*) as count FROM travel_rates WHERE country = 'Canada' AND accommodation_rate IS NOT NULL`, + perDiem: `SELECT COUNT(*) as count FROM travel_rates WHERE country != 'Canada'`, + }; + + const results = {}; + for (const [key, query] of Object.entries(queries)) { + results[key] = await new Promise((resolve, reject) => { + this.db.get(query, [], (err, row) => { + if (err) reject(err); + else resolve(row.count); }); + }); } + return results; + } - /** - * Get statistics - */ - async getStats() { - const queries = { - countries: `SELECT COUNT(DISTINCT country) as count FROM travel_rates WHERE country != 'Canada'`, - accommodations: `SELECT COUNT(*) as count FROM travel_rates WHERE country = 'Canada' AND accommodation_rate IS NOT NULL`, - perDiem: `SELECT COUNT(*) as count FROM travel_rates WHERE country != 'Canada'`, - }; - - const results = {}; - for (const [key, query] of Object.entries(queries)) { - results[key] = await new Promise((resolve, reject) => { - this.db.get(query, [], (err, row) => { - if (err) reject(err); - else resolve(row.count); - }); - }); - } - return results; - } - - close() { - if (this.db) { - this.db.close(); - console.log('āœ… Database connection closed'); - } + close() { + if (this.db) { + this.db.close(); + console.log("āœ… Database connection closed"); } + } } module.exports = new DatabaseService();