/** * County & geometry refresh — populates GisUat.county + geometry * from eTerra LIMITE_UAT layer. * * Called with an already-authenticated EterraClient (fire-and-forget * after login), so there's no session expiry risk. * * Strategy: * 1. Query LIMITE_UAT for all features WITH geometry → * get ADMIN_UNIT_ID, WORKSPACE_ID, AREA_VALUE, LAST_UPDATED_DTM + rings * 2. Map WORKSPACE_ID → county name via verified mapping * 3. Batch-update GisUat: county, workspacePk, geometry, areaValue, lastUpdatedDtm * 4. On subsequent runs: skip UATs where lastUpdatedDtm hasn't changed */ import { Prisma } from "@prisma/client"; import { prisma } from "@/core/storage/prisma"; import type { EterraClient } from "@/modules/parcel-sync/services/eterra-client"; import { findLayerById } from "@/modules/parcel-sync/services/eterra-layers"; /** * eTerra WORKSPACE_ID → Romanian county name. * * Verified by cross-referencing LIMITE_UAT sample UATs + DB confirmations. */ const WORKSPACE_TO_COUNTY: Record = { 10: "Alba", 29: "Arad", 38: "Argeș", 47: "Bacău", 56: "Bihor", 65: "Bistrița-Năsăud", 74: "Botoșani", 83: "Brașov", 92: "Brăila", 109: "Buzău", 118: "Caraș-Severin", 127: "Cluj", 136: "Constanța", 145: "Covasna", 154: "Dâmbovița", 163: "Dolj", 172: "Galați", 181: "Gorj", 190: "Harghita", 207: "Hunedoara", 216: "Ialomița", 225: "Iași", 234: "Ilfov", 243: "Maramureș", 252: "Mehedinți", 261: "Mureș", 270: "Neamț", 289: "Olt", 298: "Prahova", 305: "Satu Mare", 314: "Sălaj", 323: "Sibiu", 332: "Suceava", 341: "Teleorman", 350: "Timiș", 369: "Tulcea", 378: "Vaslui", 387: "Vâlcea", 396: "Vrancea", 403: "București", 519: "Călărași", 528: "Giurgiu", }; export async function refreshCountyData(client: EterraClient): Promise { const total = await prisma.gisUat.count(); if (total === 0) return; // Check how many are missing county OR geometry const [withCounty, withGeometry] = await Promise.all([ prisma.gisUat.count({ where: { county: { not: null } } }), prisma.gisUat.count({ where: { geometry: { not: Prisma.AnyNull } }, }), ]); const needsCounty = withCounty < total * 0.5; const needsGeometry = withGeometry < total * 0.5; if (!needsCounty && !needsGeometry) { console.log( `[county-refresh] ${withCounty}/${total} counties, ${withGeometry}/${total} geometries — skipping.`, ); return; } console.log( `[county-refresh] Starting: ${withCounty}/${total} counties, ${withGeometry}/${total} geometries.`, ); // 1. Query LIMITE_UAT — with geometry if needed, without if only county const limiteUat = findLayerById("LIMITE_UAT"); if (!limiteUat) { console.error("[county-refresh] LIMITE_UAT layer not configured."); return; } const features = await client.fetchAllLayerByWhere(limiteUat, "1=1", { outFields: "ADMIN_UNIT_ID,WORKSPACE_ID,AREA_VALUE,LAST_UPDATED_DTM", returnGeometry: needsGeometry, pageSize: 1000, }); console.log( `[county-refresh] LIMITE_UAT: ${features.length} features` + `${needsGeometry ? " (with geometry)" : ""}.`, ); if (features.length === 0) return; // 2. Log unknown workspaces const seenWs = new Set(); for (const f of features) { const ws = Number(f.attributes?.WORKSPACE_ID ?? 0); if (ws > 0 && !(ws in WORKSPACE_TO_COUNTY) && !seenWs.has(ws)) { seenWs.add(ws); const siruta = String(f.attributes?.ADMIN_UNIT_ID ?? "").replace(/\.0$/, ""); console.warn( `[county-refresh] Unknown workspace ${ws} (SIRUTA ${siruta}). Add to mapping.`, ); } } // 3. Upsert each UAT with county, workspacePk, geometry, area, lastUpdatedDtm let updated = 0; const BATCH = 50; for (let i = 0; i < features.length; i += BATCH) { const batch = features.slice(i, i + BATCH); const ops = []; for (const f of batch) { const siruta = String(f.attributes?.ADMIN_UNIT_ID ?? "") .trim() .replace(/\.0$/, ""); const ws = Number(f.attributes?.WORKSPACE_ID ?? 0); if (!siruta || ws <= 0) continue; const county = WORKSPACE_TO_COUNTY[ws]; const areaValue = Number(f.attributes?.AREA_VALUE ?? 0) || null; const lastUpdatedDtm = f.attributes?.LAST_UPDATED_DTM != null ? String(f.attributes.LAST_UPDATED_DTM) : null; // eslint-disable-next-line @typescript-eslint/no-explicit-any const geom = (f as any).geometry ?? null; const data: Prisma.GisUatUpdateInput = { workspacePk: ws, ...(county ? { county } : {}), ...(areaValue ? { areaValue } : {}), ...(lastUpdatedDtm ? { lastUpdatedDtm } : {}), ...(geom ? { geometry: geom as Prisma.InputJsonValue } : {}), }; ops.push( prisma.gisUat.updateMany({ where: { siruta }, data, }), ); } if (ops.length > 0) { const results = await prisma.$transaction(ops); for (const r of results) updated += r.count; } } console.log(`[county-refresh] Done: ${updated}/${total} updated.`); }