173 lines
4.5 KiB
TypeScript
173 lines
4.5 KiB
TypeScript
#!/usr/bin/env tsx
|
|
/**
|
|
* Import region data from CSV files into the database.
|
|
* Run after generating CSVs with scrape_regions.py
|
|
*
|
|
* Usage: npm run import:regions
|
|
*/
|
|
|
|
import { PrismaClient } from "@prisma/client";
|
|
import { readFileSync } from "fs";
|
|
import { parse } from "csv-parse/sync";
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
interface RegionRow {
|
|
id: string;
|
|
city_id: string;
|
|
name: string;
|
|
number: string;
|
|
country_id: string;
|
|
flag_id: string;
|
|
}
|
|
|
|
interface TileRow {
|
|
tile_x: string;
|
|
tile_y: string;
|
|
region_id: string;
|
|
city_id: string;
|
|
region_name: string;
|
|
region_number: string;
|
|
country_id: string;
|
|
flag_id: string;
|
|
}
|
|
|
|
async function importRegions() {
|
|
console.log("Importing regions from regions.csv...");
|
|
|
|
const csvContent = readFileSync("regions.csv", "utf-8");
|
|
const records = parse(csvContent, {
|
|
columns: true,
|
|
skip_empty_lines: true
|
|
}) as RegionRow[];
|
|
|
|
console.log(`Found ${records.length} unique regions`);
|
|
|
|
let imported = 0;
|
|
let skipped = 0;
|
|
|
|
for (const record of records) {
|
|
try {
|
|
await prisma.region.upsert({
|
|
where: { cityId: Number.parseInt(record.city_id) },
|
|
create: {
|
|
id: Number.parseInt(record.id),
|
|
cityId: Number.parseInt(record.city_id),
|
|
name: record.name,
|
|
number: Number.parseInt(record.number),
|
|
countryId: Number.parseInt(record.country_id)
|
|
},
|
|
update: {
|
|
name: record.name,
|
|
number: Number.parseInt(record.number),
|
|
countryId: Number.parseInt(record.country_id)
|
|
}
|
|
});
|
|
imported++;
|
|
} catch (error) {
|
|
console.error(`Error importing region ${record.name}:`, error);
|
|
skipped++;
|
|
}
|
|
}
|
|
|
|
console.log(`✓ Imported ${imported} regions (${skipped} skipped)`);
|
|
}
|
|
|
|
async function analyzeTileMappings() {
|
|
console.log("\nAnalyzing tile mappings from tile_region_mapping.csv...");
|
|
|
|
const csvContent = readFileSync("tile_region_mapping.csv", "utf-8");
|
|
const records = parse(csvContent, {
|
|
columns: true,
|
|
skip_empty_lines: true
|
|
}) as TileRow[];
|
|
|
|
console.log(`Found ${records.length} tile mappings`);
|
|
|
|
// Build a map of region tile counts
|
|
const regionTileCounts = new Map<number, {
|
|
name: string;
|
|
tiles: number;
|
|
minTileX: number;
|
|
maxTileX: number;
|
|
minTileY: number;
|
|
maxTileY: number;
|
|
}>();
|
|
|
|
for (const record of records) {
|
|
const regionId = Number.parseInt(record.region_id);
|
|
const tileX = Number.parseInt(record.tile_x);
|
|
const tileY = Number.parseInt(record.tile_y);
|
|
|
|
const existing = regionTileCounts.get(regionId);
|
|
if (!existing) {
|
|
regionTileCounts.set(regionId, {
|
|
name: record.region_name,
|
|
tiles: 1,
|
|
minTileX: tileX,
|
|
maxTileX: tileX,
|
|
minTileY: tileY,
|
|
maxTileY: tileY
|
|
});
|
|
} else {
|
|
existing.tiles++;
|
|
existing.minTileX = Math.min(existing.minTileX, tileX);
|
|
existing.maxTileX = Math.max(existing.maxTileX, tileX);
|
|
existing.minTileY = Math.min(existing.minTileY, tileY);
|
|
existing.maxTileY = Math.max(existing.maxTileY, tileY);
|
|
}
|
|
}
|
|
|
|
console.log("\nRegion coverage analysis:");
|
|
console.log("Region ID | Region Name | Tiles | Tile X Range | Tile Y Range");
|
|
console.log("-".repeat(90));
|
|
|
|
// Sort by tile count descending
|
|
const sorted = [...regionTileCounts.entries()].sort((a, b) => b[1].tiles - a[1].tiles);
|
|
|
|
for (const [regionId, coverage] of sorted.slice(0, 20)) {
|
|
const name = coverage.name.padEnd(24).substring(0, 24);
|
|
const xRange = `${coverage.minTileX}-${coverage.maxTileX}`.padStart(15);
|
|
const yRange = `${coverage.minTileY}-${coverage.maxTileY}`;
|
|
console.log(`${regionId.toString().padStart(9)} | ${name} | ${coverage.tiles.toString().padStart(5)} | ${xRange} | ${yRange}`);
|
|
}
|
|
|
|
if (sorted.length > 20) {
|
|
console.log(`... and ${sorted.length - 20} more regions`);
|
|
}
|
|
|
|
console.log("\n📊 This shows which tiles belong to which regions.");
|
|
console.log("💡 You can use this data to implement getRegionForCoordinates().");
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
await importRegions();
|
|
await analyzeTileMappings();
|
|
|
|
console.log("\n✅ Import complete!");
|
|
console.log("\nNext steps:");
|
|
console.log("1. Create a TileRegion lookup table in your database");
|
|
console.log("2. Import the tile_region_mapping.csv data");
|
|
console.log("3. Update getRegionForCoordinates() to query TileRegion table");
|
|
console.log("\nExample Prisma schema addition:");
|
|
console.log(`
|
|
model TileRegion {
|
|
tileX Int
|
|
tileY Int
|
|
regionId Int
|
|
region Region @relation(fields: [regionId], references: [id])
|
|
|
|
@@unique([tileX, tileY])
|
|
}
|
|
`);
|
|
} catch (error) {
|
|
console.error("Error during import:", error);
|
|
process.exit(1);
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
}
|
|
}
|
|
|
|
main();
|