Prepare scanner branch for merge

This commit is contained in:
AzuTear
2026-07-09 08:44:50 +02:00
parent f791d1464c
commit 8b73c01e46
69 changed files with 6700 additions and 3773 deletions
@@ -110,7 +110,7 @@ function shouldRecoverIntoStore(existing: StoredArtifactRecord | undefined, inco
}
function getDefaultScannerRules(loadedRules: { rules?: ScannerLearningRules } | null | undefined): ScannerLearningRules {
return { textReplacements: { ...(loadedRules?.rules?.textReplacements ?? {}) } };
return mergeLearningRulePayloads({}, loadedRules?.rules);
}
async function loadReviewSamplesAndRecover(context: ReviewStateContext, rules: ScannerLearningRules, limit = REVIEW_SAMPLE_LIMIT_INITIAL) {
@@ -213,17 +213,47 @@ export async function mergeLearningRules(
context: ReviewStateContext,
) {
if (!nextRules || countScannerLearningRules(nextRules) === 0) return null;
const merged = {
textReplacements: {
...currentRules.textReplacements,
...(nextRules.textReplacements ?? {}),
},
};
const merged = mergeLearningRulePayloads(currentRules, nextRules);
context.setScannerLearningRules(merged);
const result = await context.learningRepo?.saveRules?.(merged).catch(() => null);
return { result, merged };
}
function mergeLearningRulePayloads(
current: Partial<ScannerLearningRules> | null | undefined,
next: Partial<ScannerLearningRules> | null | undefined,
): ScannerLearningRules {
return {
textReplacements: {
...(current?.textReplacements ?? {}),
...(next?.textReplacements ?? {}),
},
fieldAliases: mergeNestedRuleMap(current?.fieldAliases, next?.fieldAliases),
constrainedFixes: {
...(current?.constrainedFixes ?? {}),
...(next?.constrainedFixes ?? {}),
},
cropAdjustments: {
...(current?.cropAdjustments ?? {}),
...(next?.cropAdjustments ?? {}),
},
uiProfileAdjustments: {
...(current?.uiProfileAdjustments ?? {}),
...(next?.uiProfileAdjustments ?? {}),
},
};
}
function mergeNestedRuleMap(
current: Record<string, Record<string, string>> | undefined,
next: Record<string, Record<string, string>> | undefined,
) {
const merged: Record<string, Record<string, string>> = {};
for (const [field, aliases] of Object.entries(current ?? {})) merged[field] = { ...(aliases ?? {}) };
for (const [field, aliases] of Object.entries(next ?? {})) merged[field] = { ...(merged[field] ?? {}), ...(aliases ?? {}) };
return merged;
}
export async function persistParsedArtifact(
capture: CaptureResult | null,
parsed: ParsedArtifactCandidate,
@@ -256,6 +286,47 @@ export async function persistParsedArtifact(
}
}
export async function persistParsedArtifactsBatch(
items: Array<{
capture: CaptureResult | null;
parsed: ParsedArtifactCandidate;
source: string;
needsReview: boolean;
}>,
context: ReviewStateContext,
) {
const { artifactRepo, onStoredArtifactsChanged, setStoredTotal, appendAutomationLog } = context;
if (!artifactRepo?.saveMany || items.length === 0) return 0;
const records: StoredArtifactRecord[] = [];
for (const item of items) {
const rejection = captureRejectionReason(item.capture, item.parsed);
if (rejection) {
appendAutomationLog(`persist skip: ${rejection}`);
continue;
}
if (!shouldPersistParsedArtifact(item.parsed, item.needsReview)) {
appendAutomationLog(`persist skip: parsed artifact bleibt vorerst nur Review (${item.parsed.name})`);
continue;
}
records.push(toStoredArtifact(item.parsed, item.source, item.needsReview, item.capture?.locked));
}
if (records.length === 0) return 0;
try {
const result = await artifactRepo.saveMany(records);
if (result?.ok) {
setStoredTotal(result.total);
void onStoredArtifactsChanged?.();
return records.length;
}
return 0;
} catch {
return 0;
}
}
export async function saveReviewSample(
capture: CaptureResult | null,
parsed: ParsedArtifactCandidate | null,