perf: reverse enrichment order — direct parcel details first, skip immApps

- fetchImmovableParcelDetails called FIRST (1 call, no applicationId needed)
- app-based fetchParcelFolosinte only as fallback when direct returns nothing
- SOLICITANT skipped entirely (was always '-' for old CF records)
- Remove unused pickApplication helper
- Net savings: ~500+ API calls per UAT enrichment (50-65% reduction)
- copycf/get returns same data as list (no enrichment value, kept as utility)
This commit is contained in:
AI Assistant
2026-03-08 01:15:28 +02:00
parent aee28b6768
commit bcc7a54325
3 changed files with 60 additions and 77 deletions
@@ -106,22 +106,6 @@ const formatAddress = (item?: any) => {
return parts.length ? parts.join(", ") : "-";
};
const pickApplication = (entries: any[], applicationId?: number) => {
if (!entries.length) return null;
if (applicationId) {
const match = entries.find(
(entry: any) => entry?.applicationId === applicationId,
);
if (match) return match;
}
return (
entries
.filter((entry: any) => entry?.dataCerere)
.sort((a: any, b: any) => (b.dataCerere ?? 0) - (a.dataCerere ?? 0))[0] ??
entries[0]
);
};
/**
* Enrichment data stored per-feature in the `enrichment` JSON column.
*/
@@ -398,7 +382,6 @@ export async function enrichFeatures(
downloaded: 0,
total: terenuri.length,
});
const immAppsCache = new Map<string, any[]>();
const folCache = new Map<string, any[]>();
let enrichedCount = 0;
let buildingCrossRefs = 0;
@@ -442,7 +425,9 @@ export async function enrichFeatures(
const workspaceId = attrs.WORKSPACE_ID ?? "";
const applicationId = (attrs.APPLICATION_ID as number) ?? null;
let solicitant = "-";
// SOLICITANT skipped — saves ~500+ API calls; value was always "-"
// for old CF records and rarely useful for modern ones.
const solicitant = "-";
let intravilan = "-";
let categorie = "-";
let proprietari = "-";
@@ -452,73 +437,62 @@ export async function enrichFeatures(
let addressText = "-";
if (immovableId && workspaceId) {
const appKey = `${workspaceId}:${immovableId}`;
let apps = immAppsCache.get(appKey);
if (!apps) {
apps = await throttled(() =>
client.fetchImmAppsByImmovable(
immovableId as string | number,
workspaceId as string | number,
),
);
immAppsCache.set(appKey, apps);
}
const chosen = pickApplication(apps, Number(applicationId ?? 0));
const appId =
chosen?.applicationId ??
(applicationId ? Number(applicationId) : null);
solicitant = chosen?.solicitant ?? chosen?.deponent ?? solicitant;
if (appId) {
const folKey = `${workspaceId}:${immovableId}:${appId}`;
let fol = folCache.get(folKey);
if (!fol) {
fol = await throttled(() =>
client.fetchParcelFolosinte(
// ── Strategy: direct parcel details FIRST (1 call, no applicationId needed) ──
// This endpoint works for both GIS features and no-geom imports.
// Saves ~50-65% of API calls vs the old app-based flow.
const immPkForDetails =
immovableListById.get(normalizeId(immovableId))?.immovablePk ??
immovableId;
const detKey = `${workspaceId}:${immPkForDetails}:details`;
let details = folCache.get(detKey);
if (!details) {
try {
details = await throttled(() =>
client.fetchImmovableParcelDetails(
workspaceId as string | number,
immovableId as string | number,
appId,
immPkForDetails as string | number,
),
);
folCache.set(folKey, fol);
} catch {
details = [];
}
folCache.set(detKey, details);
}
if (details && details.length > 0) {
intravilan = normalizeIntravilan(
fol.map((item: any) => item?.intravilan ?? ""),
details.map((d: any) => d?.intravilan ?? ""),
);
categorie = formatCategories(fol);
categorie = formatCategories(details);
}
// Fallback: if no application or empty categories, use direct
// parcel details endpoint (doesn't need applicationId).
// Discovered via eTerra UI: /api/immovable/details/parcels/list/{wp}/{pk}/{page}/{size}
// Returns: [{useCategory: "arabil", intravilan: "Necunoscut", ...}]
if (categorie === "-" && immovableId) {
const immPkForDetails =
immovableListById.get(normalizeId(immovableId))?.immovablePk ??
immovableId;
const detKey = `${workspaceId}:${immPkForDetails}:details`;
let details = folCache.get(detKey);
if (!details) {
try {
details = await throttled(() =>
client.fetchImmovableParcelDetails(
// ── Fallback: app-based flow (only if direct details returned nothing) ──
// Uses applicationId from GIS feature → fetchParcelFolosinte.
// This path adds 1 extra API call.
if (categorie === "-" && applicationId) {
const appId = Number(applicationId);
if (appId > 0) {
const folKey = `${workspaceId}:${immovableId}:${appId}`;
let fol = folCache.get(folKey);
if (!fol) {
fol = await throttled(() =>
client.fetchParcelFolosinte(
workspaceId as string | number,
immPkForDetails as string | number,
immovableId as string | number,
appId,
),
);
} catch {
details = [];
folCache.set(folKey, fol);
}
if (fol && fol.length > 0) {
const folIntravilan = normalizeIntravilan(
fol.map((item: any) => item?.intravilan ?? ""),
);
const folCategorie = formatCategories(fol);
if (folCategorie && folCategorie !== "-")
categorie = folCategorie;
if (folIntravilan && folIntravilan !== "-" && intravilan === "-")
intravilan = folIntravilan;
}
folCache.set(detKey, details);
}
if (details && details.length > 0) {
const detIntravilan = normalizeIntravilan(
details.map((d: any) => d?.intravilan ?? ""),
);
const detCategorie = formatCategories(details);
if (detCategorie && detCategorie !== "-") categorie = detCategorie;
if (detIntravilan && detIntravilan !== "-" && intravilan === "-")
intravilan = detIntravilan;
}
}
}