forked from jappel/leistungsbilanz-ts
91 lines
3.3 KiB
TypeScript
91 lines
3.3 KiB
TypeScript
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),
|
|
};
|
|
}
|