Skip unchanged Excel report uploads

This commit is contained in:
Codex 2026-07-31 08:03:30 +02:00
parent ddae04c585
commit 5d81016ece
6 changed files with 62 additions and 4 deletions

View File

@ -64,7 +64,9 @@ damit interne Dokumentdaten nicht unbeabsichtigt über einen Proxy laufen.
SHA-256-Prüfsummen und SQLite verhindern, dass erfolgreich verarbeitete Inhalte SHA-256-Prüfsummen und SQLite verhindern, dass erfolgreich verarbeitete Inhalte
erneut verarbeitet werden. Bestehende Zieldateien werden nicht überschrieben; bei erneut verarbeitet werden. Bestehende Zieldateien werden nicht überschrieben; bei
Kollisionen wird `_02`, `_03` usw. ergänzt. Eine Prozesssperre verhindert parallele Kollisionen wird `_02`, `_03` usw. ergänzt. Eine Prozesssperre verhindert parallele
Läufe auf derselben Installation. Läufe auf derselben Installation. Entsteht während eines Laufs kein neuer
Protokolleintrag, wird `protokoll.xlsx` weder lokal neu erzeugt noch erneut zu
Nextcloud hochgeladen.
## Installation auf einer Linux-VM ## Installation auf einer Linux-VM

View File

@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "orc-renaming" name = "orc-renaming"
version = "0.4.0" version = "0.4.1"
description = "Lokale, datenschutzfreundliche Benennung gescannter PDF-Dokumente" description = "Lokale, datenschutzfreundliche Benennung gescannter PDF-Dokumente"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"

View File

@ -1,3 +1,3 @@
"""Lokale Dokumentklassifikation und PDF-Benennung.""" """Lokale Dokumentklassifikation und PDF-Benennung."""
__version__ = "0.4.0" __version__ = "0.4.1"

View File

@ -91,6 +91,12 @@ class Ledger:
).fetchone() ).fetchone()
return row is not None return row is not None
def entry_count(self) -> int:
row = self.connection.execute(
"SELECT COUNT(*) AS count FROM processing_log"
).fetchone()
return int(row["count"])
def add(self, record: ProcessingRecord) -> None: def add(self, record: ProcessingRecord) -> None:
values = record.model_dump(mode="json") values = record.model_dump(mode="json")
values["evidence"] = json.dumps( values["evidence"] = json.dumps(

View File

@ -301,6 +301,7 @@ class Pipeline:
return status return status
def run(self) -> dict[str, int]: def run(self) -> dict[str, int]:
entries_before_run = self.ledger.entry_count()
upload_report = ( upload_report = (
not self.config.processing.dry_run not self.config.processing.dry_run
or self.config.processing.upload_excel_in_dry_run or self.config.processing.upload_excel_in_dry_run
@ -342,6 +343,12 @@ class Pipeline:
status = "error" status = "error"
counters[status] = counters.get(status, 0) + 1 counters[status] = counters.get(status, 0) + 1
if self.ledger.entry_count() == entries_before_run:
LOGGER.info(
"Protokoll unverändert; Excel-Export und Nextcloud-Upload übersprungen"
)
return counters
excel_path = ( excel_path = (
self.config.processing.state_directory self.config.processing.state_directory
/ self.config.processing.excel_filename / self.config.processing.excel_filename

View File

@ -4,7 +4,12 @@ from datetime import date
from pathlib import Path from pathlib import Path
import orc_renaming.pipeline as pipeline_module import orc_renaming.pipeline as pipeline_module
from orc_renaming.models import DocumentScope, DocumentType, ExtractionResult from orc_renaming.models import (
DocumentScope,
DocumentType,
ExtractionResult,
ProcessingRecord,
)
from orc_renaming.normalize import result_is_complete from orc_renaming.normalize import result_is_complete
from orc_renaming.pipeline import Pipeline from orc_renaming.pipeline import Pipeline
from orc_renaming.webdav import RemoteFile from orc_renaming.webdav import RemoteFile
@ -111,6 +116,44 @@ def test_live_run_uploads_pdf_report_and_archives(config, monkeypatch) -> None:
assert config.nextcloud.review_folder in folders assert config.nextcloud.review_folder in folders
def test_empty_run_does_not_write_or_upload_report(config, monkeypatch) -> None:
_prepare(monkeypatch)
with Pipeline(config) as pipeline:
pipeline.webdav.list_pdfs = lambda _folder: []
counters = pipeline.run()
uploads = list(pipeline.webdav.uploads)
assert counters == {}
assert uploads == []
assert not (config.processing.state_directory / "protokoll.xlsx").exists()
def test_skipped_files_do_not_rewrite_report(config, monkeypatch) -> None:
_prepare(monkeypatch)
report_path = config.processing.state_directory / "protokoll.xlsx"
with Pipeline(config) as pipeline:
pipeline.ledger.add(
ProcessingRecord(
remote_path="/Scanner/Eingang/scan001.pdf",
original_name="scan001.pdf",
checksum="abc123",
status="success",
)
)
pipeline.ledger.export_xlsx(report_path)
report_before = report_path.read_bytes()
counters = pipeline.run()
uploads = list(pipeline.webdav.uploads)
report_after = report_path.read_bytes()
assert counters == {"skipped": 1}
assert uploads == []
assert report_after == report_before
def test_ollama_fallback_is_only_used_after_incomplete_primary( def test_ollama_fallback_is_only_used_after_incomplete_primary(
config, monkeypatch config, monkeypatch
) -> None: ) -> None: