Files
Copies/copienator/annotation_read_cache.py
T

92 lines
4.4 KiB
Python

"""Small content fingerprints for incremental --update-score reads."""
from hashlib import file_digest, sha256
import json
from pathlib import Path
from copienator import atomic_write_json, configuration, read_json
from copienator.annotation_documents import copy_documents
from copienator.return_answers import load_return_answer_options
from copienator.custom_remarks import load_remarks, copy_key
from copienator import general_remarks
STATE_FILE = ".annotation-read-state.json"
DOCUMENT_INPUTS = ("bnote.json", "checkboxes.json", "Reference.jpg", "Concat_annotated.pdf")
def _files_digest(paths) -> str:
digest = sha256()
for path in sorted(set(paths)):
digest.update(str(path).encode())
if path.is_file():
with path.open("rb") as stream:
digest.update(file_digest(stream, "sha256").digest())
else:
digest.update(b"missing")
return digest.hexdigest()
class AnnotationReadCache:
state_file = STATE_FILE
def __init__(self, workspace, mode: str, data: dict, group_dirs=()):
self.workspace = workspace
self.mode = mode
self.signatures = {}
root = workspace.root
shared = [root / "labels", root / mode / "annotation_modifications_applied.txt"]
for folder in ("Text2", "Sol2"):
shared.extend((root / folder).glob("*.pdf"))
for directory in group_dirs:
shared.extend(directory / name for name in DOCUMENT_INPUTS)
common = (_files_digest(shared), load_return_answer_options(root),
configuration.RETURN_ANSWERS_ENABLED,
configuration.RETURN_JPEG_ENABLED, configuration.RETURN_PDF_ENABLED,
getattr(configuration.LATEX_ANOT_BEFORE, "template", configuration.LATEX_ANOT_BEFORE),
configuration.LATEX_ANOT_AFTER)
remarks = load_remarks(root)
general = general_remarks.load_remarks(root)
for copy_id, labels in data.items():
directory = root / mode / f"Copie{copy_id}"
paths = [directory / "score.json", directory / "bnote.json"]
for document in copy_documents(directory):
paths.extend(document / name for name in DOCUMENT_INPUTS)
for content in labels.values():
pdf = Path(content["pdf_path"])
paths.extend((pdf, pdf.with_suffix(".layout.json")))
payload = json.dumps((3, common, labels, general.get(copy_key(copy_id), []), remarks.get(copy_key(copy_id), {}), _files_digest(paths)),
sort_keys=True, default=str).encode()
self.signatures[copy_id] = sha256(payload).hexdigest()
def unchanged(self, copy_id: str) -> bool:
if not (self.workspace.root / self.mode / f"Copie{copy_id}" / "review_snapshot.json").is_file():
return False
directory = self.workspace.root / self.mode / f"Copie{copy_id}"
try:
state = read_json(directory / self.state_file)
return (state["signature"] == self.signatures[copy_id]
and bool(state["outputs"])
and all(Path(path).is_file() for path in state["outputs"]))
except (OSError, ValueError, TypeError, KeyError):
return False
def remember(self, copy_ids) -> None:
for copy_id in copy_ids:
directory = self.workspace.root / self.mode / f"Copie{copy_id}"
outputs = [directory / "info.json"]
info = read_json(directory / "info.json")
outputs.extend(directory / f"{label}.jpg" for label, entry in info.items()
if entry["present"])
outputs.extend(directory.glob(general_remarks.IMAGE_FILENAME))
outputs.extend(directory.glob("Concat*.*"))
outputs.extend(directory.glob(".annotation-base-*.png"))
outputs.extend(directory.glob("annotation_baseline.json"))
for destination in self.workspace.return_dir.glob(f"* ({copy_id})"):
outputs.extend((destination / "answers").glob("*.jpg"))
stem = destination.name.rsplit(" (", 1)[0]
outputs.extend(path for suffix in (".jpg", ".pdf")
if (path := destination / (stem + suffix)).is_file())
atomic_write_json(directory / self.state_file, {
"signature": self.signatures[copy_id],
"outputs": [str(path) for path in outputs],
})