59 lines
2.5 KiB
Python
59 lines
2.5 KiB
Python
from decimal import Decimal, InvalidOperation
|
|
from pathlib import Path
|
|
from copienator import atomic_update_json, read_json
|
|
|
|
def read_scores(path: Path) -> dict:
|
|
scores = read_json(annotation_score_for_return(path))
|
|
if not isinstance(scores, dict):
|
|
raise ValueError(f"score.json doit contenir un objet JSON : {path}")
|
|
return scores
|
|
|
|
|
|
def set_score(path: Path, label: str, value: str) -> str:
|
|
try:
|
|
number = Decimal(value.strip().replace(",", "."))
|
|
except InvalidOperation:
|
|
raise ValueError("Le score doit être un nombre.") from None
|
|
if not number.is_finite() or number < 0:
|
|
raise ValueError("Le score doit être un nombre positif ou nul.")
|
|
previous = ""
|
|
|
|
def update(scores):
|
|
nonlocal previous
|
|
if not isinstance(scores, dict) or label not in scores:
|
|
raise ValueError(f"Label absent de score.json : {label}")
|
|
previous = str(scores[label])
|
|
scores[label] = str(number)
|
|
|
|
# A Rendre/score.json is often a symlink to the annotation score file.
|
|
# Resolve it before atomic replacement so the link continues to work.
|
|
atomic_update_json(annotation_score_for_return(path).resolve(strict=True), update, default_factory=dict)
|
|
return previous
|
|
|
|
|
|
|
|
def annotation_score_for_return(path: Path) -> Path:
|
|
"""Resolve fallback return copies as well as symlinks to their source scores."""
|
|
if path.is_symlink() or path.parent.parent.name != 'A Rendre':
|
|
return path.resolve()
|
|
from copienator.return_identity import RETURN_FOLDER_PATTERN
|
|
match = RETURN_FOLDER_PATTERN.fullmatch(path.parent.name)
|
|
if not match:
|
|
return path
|
|
root = path.parent.parent.parent
|
|
cid = match['copy_id']
|
|
sources = read_json(root / '.copienator' / 'return_sources.json', default={})
|
|
mode = sources.get(str(int(cid)))
|
|
modes = (mode,) if mode in ('Bnot', 'BGnot', 'Anot') else ('Bnot', 'BGnot', 'Anot')
|
|
candidates = [p for m in modes for p in (root / m).glob('Copie*/score.json')
|
|
if p.parent.name[5:].isdigit() and int(p.parent.name[5:]) == int(cid)]
|
|
if len(candidates) > 1:
|
|
raise ValueError('Source des scores ambiguë : relancez la préparation de A Rendre dans le mode souhaité.')
|
|
return candidates[0] if candidates else path
|
|
|
|
|
|
def remember_return_source(root: Path, copy_id: str, source: Path):
|
|
path = root / '.copienator' / 'return_sources.json'
|
|
path.parent.mkdir(exist_ok=True)
|
|
atomic_update_json(path, lambda data: data.update({str(int(copy_id)): source.parent.name}), default_factory=dict)
|