fix(parcel-sync): enrichment robustness — 5 fixes for better coverage
1. Completeness check with real values: features with all "-" values are now re-enriched instead of being considered "complete" 2. Age-based re-enrichment: features older than 30 days are re-enriched on next run (catches eTerra data updates) 3. Per-feature try-catch: one feature failing no longer aborts the entire UAT enrichment — logs warning and continues 4. fetchParcelFolosinte wrapped in try-catch: was a hard failure that killed the whole enrichment process 5. Workspace resolution logging: warns when immovable list is empty (wrong workspace), warns on fallback to PK=65 These fixes should progressively improve enrichment coverage toward 100% with each weekend sync cycle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -226,9 +226,14 @@ export async function enrichFeatures(
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
// If still null, enrichment will fail gracefully with empty lists
|
||||
const workspacePkForApi = resolvedWsPk ?? 65;
|
||||
console.log(`[enrich] siruta=${siruta} workspacePk=${workspacePkForApi}`);
|
||||
if (!resolvedWsPk) {
|
||||
console.warn(
|
||||
`[enrich] siruta=${siruta}: workspace nu s-a rezolvat, folosesc fallback PK=${workspacePkForApi}`,
|
||||
);
|
||||
} else {
|
||||
console.log(`[enrich] siruta=${siruta} workspacePk=${workspacePkForApi}`);
|
||||
}
|
||||
|
||||
push({
|
||||
phase: "Pregătire îmbogățire",
|
||||
@@ -334,6 +339,18 @@ export async function enrichFeatures(
|
||||
listPage += 1;
|
||||
}
|
||||
|
||||
if (immovableListById.size === 0) {
|
||||
console.warn(
|
||||
`[enrich] siruta=${siruta}: lista de imobile e GOALĂ (workspace=${workspacePkForApi}). ` +
|
||||
`Enrichment va continua dar toate parcelele vor avea date goale. ` +
|
||||
`Verifică workspace-ul corect pentru acest UAT.`,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`[enrich] siruta=${siruta}: ${immovableListById.size} imobile găsite`,
|
||||
);
|
||||
}
|
||||
|
||||
// ── Fetch documentation/owner data ──
|
||||
push({ phase: "Descărcare documentații CF" });
|
||||
const docByImmovable = new Map<string, any>();
|
||||
@@ -392,22 +409,41 @@ export async function enrichFeatures(
|
||||
const attrs = feature.attributes as Record<string, unknown>;
|
||||
|
||||
// Skip features with complete enrichment (resume after crash/interruption).
|
||||
// Re-enrich if enrichment schema is incomplete (e.g., missing PROPRIETARI_VECHI
|
||||
// added in a later version).
|
||||
// Re-enrich if: schema incomplete, values are all "-" (empty), or older than 30 days.
|
||||
if (feature.enrichedAt != null) {
|
||||
const enrichJson = feature.enrichment as Record<string, unknown> | null;
|
||||
const isComplete =
|
||||
// Structural check: all 7 core fields must exist
|
||||
const coreFields = [
|
||||
"NR_CAD",
|
||||
"NR_CF",
|
||||
"PROPRIETARI",
|
||||
"PROPRIETARI_VECHI",
|
||||
"ADRESA",
|
||||
"CATEGORIE_FOLOSINTA",
|
||||
"HAS_BUILDING",
|
||||
];
|
||||
const structurallyComplete =
|
||||
enrichJson != null &&
|
||||
[
|
||||
"NR_CAD",
|
||||
"NR_CF",
|
||||
"PROPRIETARI",
|
||||
"PROPRIETARI_VECHI",
|
||||
"ADRESA",
|
||||
"CATEGORIE_FOLOSINTA",
|
||||
"HAS_BUILDING",
|
||||
].every((k) => k in enrichJson && enrichJson[k] !== undefined);
|
||||
if (isComplete) {
|
||||
coreFields.every((k) => k in enrichJson && enrichJson[k] !== undefined);
|
||||
|
||||
// Value check: at least some fields must have real data (not just "-")
|
||||
// A feature with ALL text fields === "-" is considered empty and needs re-enrichment
|
||||
const valueFields = ["NR_CF", "PROPRIETARI", "ADRESA", "CATEGORIE_FOLOSINTA"];
|
||||
const hasRealValues =
|
||||
enrichJson != null &&
|
||||
valueFields.some(
|
||||
(k) =>
|
||||
k in enrichJson &&
|
||||
enrichJson[k] !== undefined &&
|
||||
enrichJson[k] !== "-" &&
|
||||
enrichJson[k] !== "",
|
||||
);
|
||||
|
||||
// Age check: re-enrich if older than 30 days (catches eTerra updates)
|
||||
const ageMs = Date.now() - new Date(feature.enrichedAt).getTime();
|
||||
const isTooOld = ageMs > 30 * 24 * 60 * 60 * 1000;
|
||||
|
||||
if (structurallyComplete && hasRealValues && !isTooOld) {
|
||||
enrichedCount += 1;
|
||||
if (index % 50 === 0) {
|
||||
options?.onProgress?.(
|
||||
@@ -418,9 +454,12 @@ export async function enrichFeatures(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Stale enrichment — will be re-enriched below
|
||||
// Incomplete, empty, or stale — will be re-enriched below
|
||||
}
|
||||
|
||||
// Per-feature try-catch: one feature failing should not abort the whole UAT
|
||||
try {
|
||||
|
||||
const immovableId = attrs.IMMOVABLE_ID ?? "";
|
||||
const workspaceId = attrs.WORKSPACE_ID ?? "";
|
||||
const applicationId = (attrs.APPLICATION_ID as number) ?? null;
|
||||
@@ -474,13 +513,17 @@ export async function enrichFeatures(
|
||||
const folKey = `${workspaceId}:${immovableId}:${appId}`;
|
||||
let fol = folCache.get(folKey);
|
||||
if (!fol) {
|
||||
fol = await throttled(() =>
|
||||
client.fetchParcelFolosinte(
|
||||
workspaceId as string | number,
|
||||
immovableId as string | number,
|
||||
appId,
|
||||
),
|
||||
);
|
||||
try {
|
||||
fol = await throttled(() =>
|
||||
client.fetchParcelFolosinte(
|
||||
workspaceId as string | number,
|
||||
immovableId as string | number,
|
||||
appId,
|
||||
),
|
||||
);
|
||||
} catch {
|
||||
fol = [];
|
||||
}
|
||||
folCache.set(folKey, fol);
|
||||
}
|
||||
if (fol && fol.length > 0) {
|
||||
@@ -603,6 +646,16 @@ export async function enrichFeatures(
|
||||
});
|
||||
|
||||
enrichedCount += 1;
|
||||
|
||||
} catch (featureErr) {
|
||||
// Log and continue — don't abort the whole UAT
|
||||
const cadRef = (attrs.NATIONAL_CADASTRAL_REFERENCE ?? "?") as string;
|
||||
const msg = featureErr instanceof Error ? featureErr.message : String(featureErr);
|
||||
console.warn(
|
||||
`[enrich] Feature ${index + 1}/${terenuri.length} (cad=${cadRef}) failed: ${msg}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (index % 10 === 0) {
|
||||
push({
|
||||
phase: "Îmbogățire parcele",
|
||||
|
||||
Reference in New Issue
Block a user