diff --git a/agents/fedeo-device-agent/.env.example b/agents/fedeo-device-agent/.env.example index 80b0fb4..e37ec6f 100644 --- a/agents/fedeo-device-agent/.env.example +++ b/agents/fedeo-device-agent/.env.example @@ -10,4 +10,5 @@ FEDEO_SCAN_MODE=Color FEDEO_SCAN_SOURCE= FEDEO_SCAN_POSTPROCESS=false FEDEO_SCAN_POSTPROCESS_PROFILE=document -FEDEO_SCAN_POSTPROCESS_PYTHON=python3 +FEDEO_SCAN_POSTPROCESS_PYTHON= +FEDEO_SCAN_POSTPROCESS_STRICT=false diff --git a/agents/fedeo-device-agent/.gitignore b/agents/fedeo-device-agent/.gitignore index 94c4183..0cd0b09 100644 --- a/agents/fedeo-device-agent/.gitignore +++ b/agents/fedeo-device-agent/.gitignore @@ -1,5 +1,6 @@ dist node_modules +.venv-opencv .env *.log *.tmp diff --git a/agents/fedeo-device-agent/README.md b/agents/fedeo-device-agent/README.md index 3592b4d..b179345 100644 --- a/agents/fedeo-device-agent/README.md +++ b/agents/fedeo-device-agent/README.md @@ -58,9 +58,7 @@ npm run dev Für automatischen Zuschnitt, leichte Entzerrung, Rotation und Kontrastkorrektur kann die OpenCV-Pipeline aktiviert werden. ```bash -python3 -m venv .venv-opencv -. .venv-opencv/bin/activate -pip install -r requirements-opencv.txt +npm run setup:opencv ``` Konfiguration: @@ -69,8 +67,11 @@ Konfiguration: FEDEO_SCAN_POSTPROCESS=true FEDEO_SCAN_POSTPROCESS_PROFILE=receipt FEDEO_SCAN_POSTPROCESS_PYTHON=/pfad/zum/agent/.venv-opencv/bin/python +FEDEO_SCAN_POSTPROCESS_STRICT=false ``` +Wenn `FEDEO_SCAN_POSTPROCESS_PYTHON` leer bleibt, verwendet der Agent automatisch `.venv-opencv/bin/python`, sofern diese Umgebung existiert. Falls OpenCV nicht installiert ist und `FEDEO_SCAN_POSTPROCESS_STRICT=false` gesetzt ist, lädt der Agent den Rohscan hoch, statt den Auftrag komplett fehlschlagen zu lassen. + Profile: - `receipt`: Bons und schmale Belege werden bevorzugt hochkant zugeschnitten und kontrastiert. diff --git a/agents/fedeo-device-agent/package.json b/agents/fedeo-device-agent/package.json index b2575b1..fe8b56e 100644 --- a/agents/fedeo-device-agent/package.json +++ b/agents/fedeo-device-agent/package.json @@ -11,7 +11,8 @@ "scripts": { "build": "tsc -p tsconfig.json", "dev": "tsx src/main.ts", - "start": "node dist/main.js" + "start": "node dist/main.js", + "setup:opencv": "sh scripts/setup-opencv.sh" }, "dependencies": {}, "devDependencies": { diff --git a/agents/fedeo-device-agent/scripts/setup-opencv.sh b/agents/fedeo-device-agent/scripts/setup-opencv.sh new file mode 100644 index 0000000..9ea1764 --- /dev/null +++ b/agents/fedeo-device-agent/scripts/setup-opencv.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh +set -eu + +SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +AGENT_DIR="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)" +VENV_DIR="${FEDEO_SCAN_POSTPROCESS_VENV:-$AGENT_DIR/.venv-opencv}" +PYTHON_BIN="${PYTHON:-python3}" + +echo "FEDEO OpenCV-Umgebung wird vorbereitet" +echo "Agent: $AGENT_DIR" +echo "Venv: $VENV_DIR" + +if ! command -v "$PYTHON_BIN" >/dev/null 2>&1; then + echo "Fehler: $PYTHON_BIN wurde nicht gefunden." >&2 + exit 1 +fi + +"$PYTHON_BIN" -m venv "$VENV_DIR" +"$VENV_DIR/bin/python" -m pip install --upgrade pip +"$VENV_DIR/bin/python" -m pip install -r "$AGENT_DIR/requirements-opencv.txt" +"$VENV_DIR/bin/python" -c "import cv2, PIL, numpy; print('OpenCV OK')" + +echo +echo "Fertig. Verwende in .env:" +echo "FEDEO_SCAN_POSTPROCESS=true" +echo "FEDEO_SCAN_POSTPROCESS_PYTHON=$VENV_DIR/bin/python" diff --git a/agents/fedeo-device-agent/src/config.ts b/agents/fedeo-device-agent/src/config.ts index 7c23b64..7a6d815 100644 --- a/agents/fedeo-device-agent/src/config.ts +++ b/agents/fedeo-device-agent/src/config.ts @@ -1,8 +1,13 @@ import path from "node:path" import os from "node:os" +import { existsSync } from "node:fs" +import { fileURLToPath } from "node:url" import { AgentConfig } from "./types.js" import { loadDotEnv } from "./env.js" +const currentFile = fileURLToPath(import.meta.url) +const agentRoot = path.resolve(path.dirname(currentFile), "..") + const optional = (value: string | undefined) => { const trimmed = value?.trim() return trimmed ? trimmed : undefined @@ -30,6 +35,11 @@ const postprocessProfileFromEnv = (value: string | undefined): AgentConfig["post return "document" } +const defaultPostprocessPython = () => { + const localVenvPython = path.join(agentRoot, ".venv-opencv", "bin", "python") + return existsSync(localVenvPython) ? localVenvPython : "python3" +} + export const loadConfig = (): AgentConfig => { loadDotEnv(process.env.FEDEO_AGENT_ENV || ".env") @@ -52,6 +62,7 @@ export const loadConfig = (): AgentConfig => { scanSource: optional(process.env.FEDEO_SCAN_SOURCE), scanPostprocess: booleanFromEnv(process.env.FEDEO_SCAN_POSTPROCESS, false), postprocessProfile: postprocessProfileFromEnv(process.env.FEDEO_SCAN_POSTPROCESS_PROFILE), - postprocessPython: optional(process.env.FEDEO_SCAN_POSTPROCESS_PYTHON) || "python3", + postprocessPython: optional(process.env.FEDEO_SCAN_POSTPROCESS_PYTHON) || defaultPostprocessPython(), + postprocessStrict: booleanFromEnv(process.env.FEDEO_SCAN_POSTPROCESS_STRICT, false), } } diff --git a/agents/fedeo-device-agent/src/scan/sane.ts b/agents/fedeo-device-agent/src/scan/sane.ts index 94290a6..f3debea 100644 --- a/agents/fedeo-device-agent/src/scan/sane.ts +++ b/agents/fedeo-device-agent/src/scan/sane.ts @@ -3,6 +3,7 @@ import path from "node:path" import { AgentConfig, ScanJob, ScanResult } from "../types.js" import { commandExists, runCommand } from "../commands.js" import { hasOpenCvPostprocessRuntime, postprocessScan } from "./postprocess.js" +import { log } from "../logger.js" const mimeTypes = { pdf: "application/pdf", @@ -51,6 +52,12 @@ const ensureFilenameExtension = (filename: string, format: AgentConfig["scanForm return `${filename.slice(0, -ext.length)}${expectedExt}` } +const fallbackRawResult = (scanOutputPath: string, jobId: string): ScanResult => ({ + path: scanOutputPath, + filename: `${jobId}.raw.png`, + mimeType: "image/png", +}) + export const hasSane = () => commandExists("scanimage") export const listScanners = async () => { @@ -111,10 +118,27 @@ export const runScan = async (config: AgentConfig, job: ScanJob): Promise