Various HTML improvements
This commit is contained in:
1 parent
fd943696fb
commit
8c1fa0af57
16 files changed
+403
-45
No files matched your search
+55
-10
@@ -9,7 +9,7 @@ import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from copienator import atomic_write_json, read_json, utils
|
||||
from copienator import EvaluationWorkspace, atomic_write_json, read_json, utils
|
||||
from copienator import custom_remarks, general_remarks
|
||||
from copienator.annotation_modifications import DRAFT, APPLIED, BASELINE, read_instructions, validate_instructions
|
||||
from copienator.return_identity import return_folders, RETURN_FOLDER_PATTERN, set_copy_name
|
||||
@@ -17,8 +17,9 @@ from copienator.score_editing import set_score
|
||||
|
||||
|
||||
class Finalisation:
|
||||
def __init__(self, root: Path, mode: str, token: str, lock_fd: int | None = None):
|
||||
def __init__(self, root: Path, mode: str, token: str, lock_fd: int | None = None, *, repository: Path | None = None):
|
||||
self.root = root.resolve()
|
||||
self.repository = (repository or Path.cwd()).resolve()
|
||||
if mode not in ('Bnot', 'BGnot'):
|
||||
raise ValueError('Choisissez Bnot ou BGnot.')
|
||||
self.mode, self.token = mode, token
|
||||
@@ -80,10 +81,13 @@ class Finalisation:
|
||||
'image': (directory / f'{label}.jpg').is_file(),
|
||||
'editableBody': label in base.get('images', {}),
|
||||
'info': info.get(label, {})} for label in self.labels]})
|
||||
names_path = EvaluationWorkspace(self.root, self.repository).names_file()
|
||||
expected_names = (sorted({line.strip() for line in names_path.read_text(encoding='utf-8').splitlines()
|
||||
if line.strip()}, key=str.casefold) if names_path.is_file() else None)
|
||||
applied = self.root / self.mode / APPLIED
|
||||
applied_text = applied.read_text() if applied.exists() else f'### Source: {self.mode}\n'
|
||||
return {'evaluation': self.root.name, 'mode': self.mode, 'labels': self.labels, 'copies': copies,
|
||||
'draft': self.draft(), 'applied': applied_text, 'revision': self.revision(),
|
||||
'expectedNames': expected_names, 'draft': self.draft(), 'applied': applied_text, 'revision': self.revision(),
|
||||
'dirty': read_json(self.dirty_path, default={}).get('dirty', False), 'job': dict(self.job)}
|
||||
|
||||
def save(self, data):
|
||||
@@ -116,10 +120,20 @@ class Finalisation:
|
||||
else:
|
||||
raise ValueError('Modification inconnue.')
|
||||
self.dirty_path.parent.mkdir(exist_ok=True)
|
||||
atomic_write_json(self.dirty_path, {'dirty': True})
|
||||
pending = read_json(self.dirty_path, default={'dirty': False})
|
||||
kinds = set(pending.get('kinds', ['unknown'] if pending.get('dirty') else []))
|
||||
kinds.add(kind)
|
||||
render_inputs = None
|
||||
if kinds == {'name'}:
|
||||
render_inputs = (pending.get('render_inputs') if pending.get('kinds') == ['name']
|
||||
else self._render_inputs())
|
||||
pending = {'dirty': True, 'kinds': sorted(kinds)}
|
||||
if kinds == {'name'}:
|
||||
pending['render_inputs'] = render_inputs
|
||||
atomic_write_json(self.dirty_path, pending)
|
||||
return self.state()
|
||||
|
||||
def start_job(self, revision: str, apply_draft: bool):
|
||||
def start_job(self, revision: str, apply_draft: bool, incremental: bool = True):
|
||||
with self.mutex:
|
||||
if self.job['running'] or self.closing or revision != self.revision():
|
||||
raise ValueError('Traitement déjà en cours ou données modifiées. Rechargez la page.')
|
||||
@@ -128,12 +142,12 @@ class Finalisation:
|
||||
if not (self.root / DRAFT).exists():
|
||||
(self.root / DRAFT).write_text(self.draft())
|
||||
self.job = {'running': True, 'status': 'running', 'log': ''}
|
||||
threading.Thread(target=self._run_job, args=(apply_draft,), daemon=True).start()
|
||||
threading.Thread(target=self._run_job, args=(apply_draft, incremental), daemon=True).start()
|
||||
return dict(self.job)
|
||||
|
||||
def _run_job(self, apply_draft):
|
||||
def _run_job(self, apply_draft, incremental=True):
|
||||
try:
|
||||
self._regenerate(apply_draft)
|
||||
self._regenerate(apply_draft, incremental)
|
||||
except Exception as exc:
|
||||
with self.mutex:
|
||||
self.job.update(status='failed', log=self.job['log'] + '\n' + str(exc))
|
||||
@@ -141,7 +155,26 @@ class Finalisation:
|
||||
with self.mutex:
|
||||
self.job['running'] = False
|
||||
|
||||
def _regenerate(self, apply_draft):
|
||||
def _render_inputs(self):
|
||||
from copienator.reviewed_cache import ReviewedReadCache
|
||||
from copienator.review_snapshot import FILENAME
|
||||
directories = self.copies()
|
||||
if not directories or not all((d / FILENAME).is_file() for d in directories):
|
||||
return None
|
||||
cache = ReviewedReadCache(EvaluationWorkspace(self.root), self.mode, directories)
|
||||
# Keep the fast path conservative when an existing rendered file disappears.
|
||||
outputs = []
|
||||
for directory in directories:
|
||||
info = read_json(directory / 'info.json', default={})
|
||||
paths = [directory / 'info.json', directory / BASELINE]
|
||||
paths += [directory / f'{label}.jpg' for label, entry in info.items() if entry['present']]
|
||||
paths += list(directory.glob('Concat*.*'))
|
||||
if any(not path.is_file() for path in paths):
|
||||
return None
|
||||
outputs.extend(str(p) for p in paths)
|
||||
return {'signatures': cache.signatures, 'outputs': sorted(outputs)}
|
||||
|
||||
def _regenerate(self, apply_draft, incremental=True):
|
||||
from copienator.return_answers import load_return_answer_options
|
||||
options = load_return_answer_options(self.root)
|
||||
reader = 'read-grouped' if self.mode == 'BGnot' else 'read-annotations'
|
||||
@@ -152,13 +185,25 @@ class Finalisation:
|
||||
if apply_draft:
|
||||
commands.append(['modify-annotations', str(self.root), '--annotation-dir', self.mode])
|
||||
commands.append(['giving-names', str(self.root), self.mode])
|
||||
pending = read_json(self.dirty_path, default={})
|
||||
names_only = (incremental and not apply_draft and pending.get('kinds') == ['name']
|
||||
and pending.get('render_inputs') is not None
|
||||
and pending['render_inputs'] == self._render_inputs())
|
||||
from copienator.review_snapshot import FILENAME
|
||||
if all((directory / FILENAME).is_file() for directory in self.copies()):
|
||||
if names_only:
|
||||
commands = [['giving-names', str(self.root), self.mode]]
|
||||
with self.mutex:
|
||||
self.job['log'] += 'Noms seuls modifiés : conservation des images et PDF de correction.\n'
|
||||
elif all((directory / FILENAME).is_file() for directory in self.copies()):
|
||||
commands[0] = ['regenerate-reviewed', str(self.root), '--annotation-dir', self.mode]
|
||||
elif (self.root / 'BRnot').is_dir():
|
||||
with self.mutex:
|
||||
self.job.update(running=False, status='failed', log='Résultat de lecture sauvegardé absent pour une évaluation avec reprise. Relisez le passage principal puis fusionnez la reprise avant de régénérer dans le navigateur. Aucun résultat n’a été remplacé.')
|
||||
return
|
||||
if incremental:
|
||||
for command in commands:
|
||||
if command[0] in ('regenerate-reviewed', 'modify-annotations', 'giving-names'):
|
||||
command.append('--incremental')
|
||||
env = dict(os.environ, COPIENATOR_FINALISATION_TOKEN=self.token, PYTHONUNBUFFERED='1')
|
||||
from copienator import configuration
|
||||
if configuration.CONFIG_PATH is not None:
|
||||
|
||||
Reference in new issue
Block a user