Add Revit CSV preview API

This commit is contained in:
Julian Appel 2026-08-02 16:49:42 +02:00
parent 219da5ec3b
commit caa6caf99f
14 changed files with 437 additions and 3 deletions

View file

@ -0,0 +1,91 @@
import { createHash } from "node:crypto";
import type { ExternalCsvConfiguration } from "../csv/external-csv-contracts.js";
import { parseExternalCsv } from "../csv/external-csv-transport.js";
export interface ExternalCsvPreviewObject {
rowNumber: number;
ifcGuid: string;
roomNumber: string;
roomName: string;
familyAndType: string;
selectionMarker: string;
circuitIdentifier: string;
power: string;
quantity: string | null;
additionalSourceValues: Record<string, string>;
}
export interface ExternalCsvPreview {
fileName: string;
byteLength: number;
sha256: string;
dialect: ReturnType<typeof parseExternalCsv>["dialect"];
headerRowNumber: number;
headerColumns: string[];
rowCount: number;
objectCount: number;
passthroughCount: number;
nonEmptyPassthroughCount: number;
suspectObjectCount: number;
objects: ExternalCsvPreviewObject[];
suspectRowNumbers: number[];
}
export function createExternalCsvPreview(input: {
fileName: string;
bytes: Uint8Array;
configuration: ExternalCsvConfiguration;
}): ExternalCsvPreview {
const document = parseExternalCsv(input.bytes, input.configuration);
const header = document.rows[document.headerRowIndex];
const headerIndex = new Map(
header.cells.map((cell, index) => [cell.value, index])
);
const valueAt = (row: (typeof document.rows)[number], column: string) =>
row.cells[headerIndex.get(column)!]?.value ?? "";
const objects = document.rows
.filter((row) => row.classification === "object")
.map((row): ExternalCsvPreviewObject => ({
rowNumber: row.index + 1,
ifcGuid: valueAt(row, input.configuration.columns.ifcGuid),
roomNumber: valueAt(row, input.configuration.columns.roomNumber),
roomName: valueAt(row, input.configuration.columns.roomName),
familyAndType: valueAt(row, input.configuration.columns.familyAndType),
selectionMarker: valueAt(row, input.configuration.columns.selectionMarker),
circuitIdentifier: valueAt(row, input.configuration.columns.circuitIdentifier),
power: valueAt(row, input.configuration.columns.power),
quantity:
input.configuration.columns.quantity === null
? null
: valueAt(row, input.configuration.columns.quantity),
additionalSourceValues: Object.fromEntries(
input.configuration.additionalSourceMappings.map((mapping) => [
mapping.targetField,
valueAt(row, mapping.sourceColumn),
])
),
}));
const passthroughRows = document.rows.filter(
(row) => row.classification === "passthrough"
);
const suspectRows = document.rows.filter(
(row) => row.classification === "suspect-object"
);
return {
fileName: input.fileName,
byteLength: input.bytes.byteLength,
sha256: createHash("sha256").update(input.bytes).digest("hex"),
dialect: document.dialect,
headerRowNumber: document.headerRowIndex + 1,
headerColumns: header.cells.map((cell) => cell.value),
rowCount: document.rows.length,
objectCount: objects.length,
passthroughCount: passthroughRows.length,
nonEmptyPassthroughCount: passthroughRows.filter((row) =>
row.cells.some((cell) => cell.value !== "")
).length,
suspectObjectCount: suspectRows.length,
objects,
suspectRowNumbers: suspectRows.map((row) => row.index + 1),
};
}