af2631920f
- Filter no-geom items before import: must have identification (cadRef/CF/paperCad/paperLb) OR area - Multi-field area extraction: area, measuredArea, areaValue, suprafata - Scan quality breakdown: withCadRef, withPaperCf, withPaperCad, withArea, useful, empty - Added paperLbNo to quality analysis and samples - UI: quality breakdown grid in scan card - UI: filtered count in workflow preview (shows useful, not total) - UI: enrichment estimate uses useful count - New diagnostic endpoint /api/eterra/no-geom-debug for field inspection
174 lines
5.0 KiB
TypeScript
174 lines
5.0 KiB
TypeScript
/**
|
|
* POST /api/eterra/no-geom-debug
|
|
*
|
|
* Diagnostic endpoint: fetches a small sample of no-geometry immovables
|
|
* and returns ALL their raw fields, so we can understand what data is
|
|
* available for quality filtering.
|
|
*
|
|
* Body: { siruta: string, sampleSize?: number }
|
|
* Returns: { sample: [...raw items], allFields: [...field names], fieldStats: {...} }
|
|
*/
|
|
|
|
import { NextResponse } from "next/server";
|
|
import { EterraClient } from "@/modules/parcel-sync/services/eterra-client";
|
|
import { getSessionCredentials } from "@/modules/parcel-sync/services/session-store";
|
|
|
|
export const runtime = "nodejs";
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export async function POST(req: Request) {
|
|
try {
|
|
const body = (await req.json()) as {
|
|
siruta?: string;
|
|
sampleSize?: number;
|
|
};
|
|
const siruta = String(body.siruta ?? "").trim();
|
|
if (!/^\d+$/.test(siruta)) {
|
|
return NextResponse.json(
|
|
{ error: "SIRUTA must be numeric" },
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
|
|
const session = getSessionCredentials();
|
|
const username = String(
|
|
session?.username || process.env.ETERRA_USERNAME || "",
|
|
).trim();
|
|
const password = String(
|
|
session?.password || process.env.ETERRA_PASSWORD || "",
|
|
).trim();
|
|
if (!username || !password) {
|
|
return NextResponse.json(
|
|
{ error: "Nu ești conectat la eTerra" },
|
|
{ status: 401 },
|
|
);
|
|
}
|
|
|
|
const client = await EterraClient.create(username, password);
|
|
|
|
// Fetch first page of immovables
|
|
const response = await client.fetchImmovableListByAdminUnit(
|
|
// We need workspace PK — try to resolve
|
|
0, // placeholder, we'll get from the sample
|
|
siruta,
|
|
0,
|
|
20,
|
|
true, // inscrisCF = -1
|
|
);
|
|
|
|
// If workspace 0 doesn't work, try to resolve it
|
|
let items = response?.content ?? [];
|
|
if (items.length === 0) {
|
|
// Try without inscrisCF filter
|
|
const response2 = await client.fetchImmovableListByAdminUnit(
|
|
0,
|
|
siruta,
|
|
0,
|
|
20,
|
|
false,
|
|
);
|
|
items = response2?.content ?? [];
|
|
}
|
|
|
|
// If still empty, try to get workspace from GIS layer
|
|
if (items.length === 0) {
|
|
const features = await client.fetchAllLayer(
|
|
{
|
|
id: "TERENURI_ACTIVE",
|
|
name: "TERENURI_ACTIVE",
|
|
endpoint: "aut" as const,
|
|
whereTemplate: "{{adminField}}={{siruta}} AND IS_ACTIVE=1",
|
|
},
|
|
siruta,
|
|
{ returnGeometry: false, outFields: "WORKSPACE_ID", pageSize: 1 },
|
|
);
|
|
const wsId = features?.[0]?.attributes?.WORKSPACE_ID;
|
|
if (wsId) {
|
|
const response3 = await client.fetchImmovableListByAdminUnit(
|
|
Number(wsId),
|
|
siruta,
|
|
0,
|
|
20,
|
|
true,
|
|
);
|
|
items = response3?.content ?? [];
|
|
}
|
|
}
|
|
|
|
if (items.length === 0) {
|
|
return NextResponse.json({
|
|
error: "Nu s-au găsit imobile",
|
|
raw: response,
|
|
});
|
|
}
|
|
|
|
// Analyze ALL fields across all items
|
|
const allFields = new Set<string>();
|
|
const fieldStats: Record<
|
|
string,
|
|
{ present: number; nonNull: number; nonEmpty: number; sample: unknown }
|
|
> = {};
|
|
|
|
for (const item of items) {
|
|
if (typeof item !== "object" || item == null) continue;
|
|
for (const [key, value] of Object.entries(item)) {
|
|
allFields.add(key);
|
|
if (!fieldStats[key]) {
|
|
fieldStats[key] = {
|
|
present: 0,
|
|
nonNull: 0,
|
|
nonEmpty: 0,
|
|
sample: undefined,
|
|
};
|
|
}
|
|
const stat = fieldStats[key]!;
|
|
stat.present++;
|
|
if (value != null) {
|
|
stat.nonNull++;
|
|
if (stat.sample === undefined) stat.sample = value;
|
|
}
|
|
if (value != null && value !== "" && value !== 0) stat.nonEmpty++;
|
|
}
|
|
}
|
|
|
|
// Also try to get GIS features for comparison
|
|
const gisFeatures = await client.fetchAllLayer(
|
|
{
|
|
id: "TERENURI_ACTIVE",
|
|
name: "TERENURI_ACTIVE",
|
|
endpoint: "aut" as const,
|
|
whereTemplate: "{{adminField}}={{siruta}} AND IS_ACTIVE=1",
|
|
},
|
|
siruta,
|
|
{
|
|
returnGeometry: false,
|
|
outFields:
|
|
"OBJECTID,NATIONAL_CADASTRAL_REFERENCE,IMMOVABLE_ID,AREA_VALUE",
|
|
pageSize: 5,
|
|
},
|
|
);
|
|
|
|
const gisFieldStats: Record<string, unknown> = {};
|
|
if (gisFeatures.length > 0) {
|
|
const sample = gisFeatures[0]!.attributes;
|
|
for (const [key, value] of Object.entries(sample ?? {})) {
|
|
gisFieldStats[key] = value;
|
|
}
|
|
}
|
|
|
|
const sampleSize = Math.min(body.sampleSize ?? 5, items.length);
|
|
return NextResponse.json({
|
|
totalItems: response?.totalElements ?? items.length,
|
|
totalPages: response?.totalPages ?? 1,
|
|
sampleCount: sampleSize,
|
|
sample: items.slice(0, sampleSize),
|
|
allFields: Array.from(allFields).sort(),
|
|
fieldStats,
|
|
gisSampleFields: gisFieldStats,
|
|
});
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : "Eroare server";
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|