name generation improvement
This commit is contained in:
+78
-45
@@ -1,6 +1,6 @@
|
|||||||
import { unzip } from "fflate";
|
import { unzip } from 'fflate';
|
||||||
|
|
||||||
export type ZipAttachmentKind = "pdf" | "image";
|
export type ZipAttachmentKind = 'pdf' | 'image';
|
||||||
|
|
||||||
export type ZipAttachment = {
|
export type ZipAttachment = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -10,25 +10,31 @@ export type ZipAttachment = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ProcessedZipArchive = {
|
export type ProcessedZipArchive = {
|
||||||
name: string
|
name: string;
|
||||||
attachments: ZipAttachment[]
|
attachments: ZipAttachment[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type XJustizMetadata = {
|
||||||
|
sender: string;
|
||||||
|
receiver: string;
|
||||||
|
documentNames: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const IMAGE_EXTENSIONS = new Set([
|
const IMAGE_EXTENSIONS = new Set([
|
||||||
".avif",
|
'.avif',
|
||||||
".bmp",
|
'.bmp',
|
||||||
".gif",
|
'.gif',
|
||||||
".heic",
|
'.heic',
|
||||||
".jpeg",
|
'.jpeg',
|
||||||
".jpg",
|
'.jpg',
|
||||||
".jp2",
|
'.jp2',
|
||||||
".png",
|
'.png',
|
||||||
".tif",
|
'.tif',
|
||||||
".tiff",
|
'.tiff',
|
||||||
".webp",
|
'.webp'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const ZIP_META_FILE_NAME = "xjustiz_nachricht.xml";
|
const ZIP_META_FILE_NAME = 'xjustiz_nachricht.xml';
|
||||||
const XML_TEXT_DECODER = new TextDecoder();
|
const XML_TEXT_DECODER = new TextDecoder();
|
||||||
|
|
||||||
const unzipArchive = (data: Uint8Array) =>
|
const unzipArchive = (data: Uint8Array) =>
|
||||||
@@ -43,37 +49,37 @@ const unzipArchive = (data: Uint8Array) =>
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const getBaseName = (path: string) => path.split("/").pop() ?? path;
|
const getBaseName = (path: string) => path.split('/').pop() ?? path;
|
||||||
|
|
||||||
const getExtension = (path: string) => {
|
const getExtension = (path: string) => {
|
||||||
const baseName = getBaseName(path).toLowerCase();
|
const baseName = getBaseName(path).toLowerCase();
|
||||||
const lastDotIndex = baseName.lastIndexOf(".");
|
const lastDotIndex = baseName.lastIndexOf('.');
|
||||||
|
|
||||||
return lastDotIndex === -1 ? "" : baseName.slice(lastDotIndex);
|
return lastDotIndex === -1 ? '' : baseName.slice(lastDotIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isPdf = (path: string) => getExtension(path) === ".pdf";
|
const isPdf = (path: string) => getExtension(path) === '.pdf';
|
||||||
|
|
||||||
const isImage = (path: string) => IMAGE_EXTENSIONS.has(getExtension(path));
|
const isImage = (path: string) => IMAGE_EXTENSIONS.has(getExtension(path));
|
||||||
|
|
||||||
const isZipArchive = (path: string) => getExtension(path) === ".zip";
|
const isZipArchive = (path: string) => getExtension(path) === '.zip';
|
||||||
|
|
||||||
const normalizeKey = (value: string) => value.replaceAll("\\", "/").toLowerCase().trim();
|
const normalizeKey = (value: string) => value.replaceAll('\\', '/').toLowerCase().trim();
|
||||||
|
|
||||||
const isMacMetadataEntry = (path: string) => {
|
const isMacMetadataEntry = (path: string) => {
|
||||||
const normalizedPath = normalizeKey(path);
|
const normalizedPath = normalizeKey(path);
|
||||||
const baseName = getBaseName(normalizedPath);
|
const baseName = getBaseName(normalizedPath);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
normalizedPath.startsWith("__macosx/") ||
|
normalizedPath.startsWith('__macosx/') ||
|
||||||
normalizedPath.includes("/__macosx/") ||
|
normalizedPath.includes('/__macosx/') ||
|
||||||
baseName === ".ds_store" ||
|
baseName === '.ds_store' ||
|
||||||
baseName.startsWith("._")
|
baseName.startsWith('._')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const matchesElementName = (element: Element, requestedName: string) => {
|
const matchesElementName = (element: Element, requestedName: string) => {
|
||||||
const requestedLocalName = requestedName.split(".").pop() ?? requestedName;
|
const requestedLocalName = requestedName.split('.').pop() ?? requestedName;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
element.localName === requestedName ||
|
element.localName === requestedName ||
|
||||||
@@ -88,7 +94,9 @@ const getDirectChildByLocalName = (element: Element, localName: string) =>
|
|||||||
Array.from(element.children).find((child) => matchesElementName(child, localName)) ?? null;
|
Array.from(element.children).find((child) => matchesElementName(child, localName)) ?? null;
|
||||||
|
|
||||||
const getFirstDescendantByLocalName = (root: Document | Element, localName: string) =>
|
const getFirstDescendantByLocalName = (root: Document | Element, localName: string) =>
|
||||||
Array.from(root.querySelectorAll("*")).find((element) => matchesElementName(element, localName)) ?? null;
|
Array.from(root.querySelectorAll('*')).find((element) =>
|
||||||
|
matchesElementName(element, localName)
|
||||||
|
) ?? null;
|
||||||
|
|
||||||
const getTextContentByPath = (root: Element, path: string[]) => {
|
const getTextContentByPath = (root: Element, path: string[]) => {
|
||||||
let current: Element | null = root;
|
let current: Element | null = root;
|
||||||
@@ -106,37 +114,54 @@ const getTextContentByPath = (root: Element, path: string[]) => {
|
|||||||
return textContent ? textContent : null;
|
return textContent ? textContent : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTextContentByLocalName = (root: Document | Element, localName: string) =>
|
||||||
|
getFirstDescendantByLocalName(root, localName)?.textContent?.trim() ?? null;
|
||||||
|
|
||||||
const parseXmlDocument = (xmlBytes: Uint8Array) => {
|
const parseXmlDocument = (xmlBytes: Uint8Array) => {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const document = parser.parseFromString(XML_TEXT_DECODER.decode(xmlBytes), "application/xml");
|
const document = parser.parseFromString(XML_TEXT_DECODER.decode(xmlBytes), 'application/xml');
|
||||||
|
|
||||||
if (document.querySelector("parsererror")) {
|
if (document.querySelector('parsererror')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getXJustizDocumentNames = (xmlBytes: Uint8Array) => {
|
const parseXJustizMetadata = (xmlBytes: Uint8Array): XJustizMetadata | null => {
|
||||||
const document = parseXmlDocument(xmlBytes);
|
const document = parseXmlDocument(xmlBytes);
|
||||||
|
|
||||||
if (!document) {
|
if (!document) {
|
||||||
return [];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const schriftgutobjekte = getFirstDescendantByLocalName(document, "schriftgutobjekte");
|
const sender = getTextContentByLocalName(document, 'aktenzeichen.absender') ?? '';
|
||||||
|
const receiver = getTextContentByLocalName(document, 'aktenzeichen.empfaenger') ?? '';
|
||||||
|
const schriftgutobjekte = getFirstDescendantByLocalName(document, 'schriftgutobjekte');
|
||||||
|
|
||||||
if (!schriftgutobjekte) {
|
if (!schriftgutobjekte) {
|
||||||
return [];
|
return {
|
||||||
|
sender,
|
||||||
|
receiver,
|
||||||
|
documentNames: []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const dokumentNodes = Array.from(schriftgutobjekte.children).filter((child) =>
|
const dokumentNodes = Array.from(schriftgutobjekte.children).filter((child) =>
|
||||||
matchesElementName(child, "dokument"),
|
matchesElementName(child, 'dokument')
|
||||||
);
|
);
|
||||||
|
|
||||||
return dokumentNodes
|
const documentNames = dokumentNodes
|
||||||
.map((dokument) => getTextContentByPath(dokument, ["xjustiz.fachspezifischeDaten", "datei", "dateiname"]))
|
.map((dokument) =>
|
||||||
|
getTextContentByPath(dokument, ['xjustiz.fachspezifischeDaten', 'datei', 'dateiname'])
|
||||||
|
)
|
||||||
.filter((name): name is string => Boolean(name));
|
.filter((name): name is string => Boolean(name));
|
||||||
|
|
||||||
|
return {
|
||||||
|
sender,
|
||||||
|
receiver,
|
||||||
|
documentNames
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const orderAttachmentsByDocumentNames = (attachments: ZipAttachment[], documentNames: string[]) => {
|
const orderAttachmentsByDocumentNames = (attachments: ZipAttachment[], documentNames: string[]) => {
|
||||||
@@ -190,7 +215,10 @@ const orderAttachmentsByDocumentNames = (attachments: ZipAttachment[], documentN
|
|||||||
return orderedAttachments;
|
return orderedAttachments;
|
||||||
};
|
};
|
||||||
|
|
||||||
const extractZipEntries = async (archiveBytes: Uint8Array, fallbackName: string): Promise<ProcessedZipArchive[]> => {
|
const extractZipEntries = async (
|
||||||
|
archiveBytes: Uint8Array,
|
||||||
|
_fallbackName: string
|
||||||
|
): Promise<ProcessedZipArchive[]> => {
|
||||||
const files = await unzipArchive(archiveBytes);
|
const files = await unzipArchive(archiveBytes);
|
||||||
const attachments: ZipAttachment[] = [];
|
const attachments: ZipAttachment[] = [];
|
||||||
const nestedArchives: ProcessedZipArchive[] = [];
|
const nestedArchives: ProcessedZipArchive[] = [];
|
||||||
@@ -218,8 +246,8 @@ const extractZipEntries = async (archiveBytes: Uint8Array, fallbackName: string)
|
|||||||
attachments.push({
|
attachments.push({
|
||||||
name: baseName,
|
name: baseName,
|
||||||
path,
|
path,
|
||||||
kind: "pdf",
|
kind: 'pdf',
|
||||||
data,
|
data
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -228,21 +256,26 @@ const extractZipEntries = async (archiveBytes: Uint8Array, fallbackName: string)
|
|||||||
attachments.push({
|
attachments.push({
|
||||||
name: baseName,
|
name: baseName,
|
||||||
path,
|
path,
|
||||||
kind: "image",
|
kind: 'image',
|
||||||
data,
|
data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentNames = xjustizNachrichtXml ? getXJustizDocumentNames(xjustizNachrichtXml) : [];
|
const metadata = xjustizNachrichtXml ? parseXJustizMetadata(xjustizNachrichtXml) : null;
|
||||||
|
const documentNames = metadata?.documentNames ?? [];
|
||||||
const orderedAttachments = orderAttachmentsByDocumentNames(attachments, documentNames);
|
const orderedAttachments = orderAttachmentsByDocumentNames(attachments, documentNames);
|
||||||
const archiveName = documentNames[0] ? getBaseName(documentNames[0]) : fallbackName;
|
const now = new Date();
|
||||||
|
const date = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
||||||
|
const archiveName = metadata
|
||||||
|
? `${date}_${metadata.sender}_${metadata.receiver}.pdf`
|
||||||
|
: `${date}_unbekannt_unbekannt.pdf`;
|
||||||
const archives: ProcessedZipArchive[] = [];
|
const archives: ProcessedZipArchive[] = [];
|
||||||
|
|
||||||
if (orderedAttachments.length > 0) {
|
if (orderedAttachments.length > 0) {
|
||||||
archives.push({
|
archives.push({
|
||||||
name: archiveName,
|
name: archiveName,
|
||||||
attachments: orderedAttachments,
|
attachments: orderedAttachments
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user