Files
Copies/copienator/score_editing.py
T

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)