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 */
|
/* ignore */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If still null, enrichment will fail gracefully with empty lists
|
|
||||||
const workspacePkForApi = resolvedWsPk ?? 65;
|
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({
|
push({
|
||||||
phase: "Pregătire îmbogățire",
|
phase: "Pregătire îmbogățire",
|
||||||
@@ -334,6 +339,18 @@ export async function enrichFeatures(
|
|||||||
listPage += 1;
|
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 ──
|
// ── Fetch documentation/owner data ──
|
||||||
push({ phase: "Descărcare documentații CF" });
|
push({ phase: "Descărcare documentații CF" });
|
||||||
const docByImmovable = new Map<string, any>();
|
const docByImmovable = new Map<string, any>();
|
||||||
@@ -392,22 +409,41 @@ export async function enrichFeatures(
|
|||||||
const attrs = feature.attributes as Record<string, unknown>;
|
const attrs = feature.attributes as Record<string, unknown>;
|
||||||
|
|
||||||
// Skip features with complete enrichment (resume after crash/interruption).
|
// Skip features with complete enrichment (resume after crash/interruption).
|
||||||
// Re-enrich if enrichment schema is incomplete (e.g., missing PROPRIETARI_VECHI
|
// Re-enrich if: schema incomplete, values are all "-" (empty), or older than 30 days.
|
||||||
// added in a later version).
|
|
||||||
if (feature.enrichedAt != null) {
|
if (feature.enrichedAt != null) {
|
||||||
const enrichJson = feature.enrichment as Record<string, unknown> | 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 &&
|
enrichJson != null &&
|
||||||
[
|
coreFields.every((k) => k in enrichJson && enrichJson[k] !== undefined);
|
||||||
"NR_CAD",
|
|
||||||
"NR_CF",
|
// Value check: at least some fields must have real data (not just "-")
|
||||||
"PROPRIETARI",
|
// A feature with ALL text fields === "-" is considered empty and needs re-enrichment
|
||||||
"PROPRIETARI_VECHI",
|
const valueFields = ["NR_CF", "PROPRIETARI", "ADRESA", "CATEGORIE_FOLOSINTA"];
|
||||||
"ADRESA",
|
const hasRealValues =
|
||||||
"CATEGORIE_FOLOSINTA",
|
enrichJson != null &&
|
||||||
"HAS_BUILDING",
|
valueFields.some(
|
||||||
].every((k) => k in enrichJson && enrichJson[k] !== undefined);
|
(k) =>
|
||||||
if (isComplete) {
|
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;
|
enrichedCount += 1;
|
||||||
if (index % 50 === 0) {
|
if (index % 50 === 0) {
|
||||||
options?.onProgress?.(
|
options?.onProgress?.(
|
||||||
@@ -418,9 +454,12 @@ export async function enrichFeatures(
|
|||||||
}
|
}
|
||||||
continue;
|
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 immovableId = attrs.IMMOVABLE_ID ?? "";
|
||||||
const workspaceId = attrs.WORKSPACE_ID ?? "";
|
const workspaceId = attrs.WORKSPACE_ID ?? "";
|
||||||
const applicationId = (attrs.APPLICATION_ID as number) ?? null;
|
const applicationId = (attrs.APPLICATION_ID as number) ?? null;
|
||||||
@@ -474,13 +513,17 @@ export async function enrichFeatures(
|
|||||||
const folKey = `${workspaceId}:${immovableId}:${appId}`;
|
const folKey = `${workspaceId}:${immovableId}:${appId}`;
|
||||||
let fol = folCache.get(folKey);
|
let fol = folCache.get(folKey);
|
||||||
if (!fol) {
|
if (!fol) {
|
||||||
fol = await throttled(() =>
|
try {
|
||||||
client.fetchParcelFolosinte(
|
fol = await throttled(() =>
|
||||||
workspaceId as string | number,
|
client.fetchParcelFolosinte(
|
||||||
immovableId as string | number,
|
workspaceId as string | number,
|
||||||
appId,
|
immovableId as string | number,
|
||||||
),
|
appId,
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
fol = [];
|
||||||
|
}
|
||||||
folCache.set(folKey, fol);
|
folCache.set(folKey, fol);
|
||||||
}
|
}
|
||||||
if (fol && fol.length > 0) {
|
if (fol && fol.length > 0) {
|
||||||
@@ -603,6 +646,16 @@ export async function enrichFeatures(
|
|||||||
});
|
});
|
||||||
|
|
||||||
enrichedCount += 1;
|
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) {
|
if (index % 10 === 0) {
|
||||||
push({
|
push({
|
||||||
phase: "Îmbogățire parcele",
|
phase: "Îmbogățire parcele",
|
||||||
|
|||||||
Reference in New Issue
Block a user