OPEN SOURCE
software/src/output/repair_review.py
Read on this site and download the files you need.
Nichijoyaku was created using AI. A complete review by human biblical-language and biblical-studies specialists has not been established. Read alongside established translations and original-language resources.
← Back to the open-source guide
1"""Import complete, text-bound editorial decisions into the alignment manifest."""2from __future__ import annotations34from copy import deepcopy5import hashlib6import json7from pathlib import Path89from src.output.alignment import candidate_hashes, digest, validate_repair101112def indexed(rows):13result = {row['target']: row for row in rows}14if len(result) != len(rows):15raise ValueError('Duplicate review targets')16return result171819def import_bundle(review_path, bundle_path, out):20review_path, bundle_path, out = Path(review_path), Path(bundle_path), Path(out).resolve()21if out in (review_path.resolve(), bundle_path.resolve()):22raise ValueError('Imported review must not overwrite its evidence inputs')23original = review_path.read_bytes()24evidence = bundle_path.read_bytes()25bundle = json.loads(evidence)26if hashlib.sha256(original).hexdigest() != bundle['input_review_sha256']:27raise ValueError('Repair bundle belongs to a different input review')28review = json.loads(original)29result = merge_repairs(review, bundle['packets'], bundle['decisions'], bundle['verification'],30hashlib.sha256(evidence).hexdigest())31result['repair_source_files'] = bundle['source_files']32result['usage'] = {'initial_alignment': review['usage'], 'repair_round': bundle['usage']}33out.parent.mkdir(parents=True, exist_ok=True)34out.write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n')35return len(result['target_repairs'])363738def merge_repairs(review, packets, decisions, verification, evidence_sha256):39"""Require a passing verification of each exact final text before import."""40packets, decisions, verification = map(indexed, (packets, decisions, verification))41if packets.keys() != decisions.keys() or packets.keys() != verification.keys():42raise ValueError('Incomplete or unexpected repair review targets')43result = deepcopy(review)44repairs = result.setdefault('target_repairs', {})45for target, packet in packets.items():46decision, check = decisions[target], verification[target]47if check['status'] != 'pass' or check['text_sha256'] != digest(decision['text']):48raise ValueError(f'Repair lacks passing verification of final text: {target}')49if decision.get('boundary_issue') and not check.get('boundary_resolution'):50raise ValueError(f'Unresolved source boundary: {target}')51selected = decision['selected_sources']52choices = {item['source']: item['text'] for item in packet['candidates']}53if selected and (len(set(selected)) != len(selected) or set(selected) - choices.keys() or54''.join(choices[source] for source in selected) != decision['text']):55raise ValueError(f'Selected text does not match candidate sources: {target}')56repair = {'text': decision['text'], 'expected_candidates': candidate_hashes(packet['candidates']),57'selected_sources': selected, 'reason': decision['reason'],58'evidence_sha256': evidence_sha256, 'verified_by': check['reviewer'],59'verification': check, 'original_sources': packet['source_spans']}60validate_repair(target, repair, packet['candidates'])61repairs[target] = repair62for source in packet.get('held_sources', []):63prior = result['records'][source]64if prior['mapped_targets'] != [target] or prior['decision'] not in ('mixed', 'unclear'):65raise ValueError(f'Held record mapping changed: {source}')66prior['pre_repair_review'] = deepcopy(prior)67prior.update(decision='mapped', reviewer=check['reviewer'],68reason=f'Original ambiguous candidate retained in audit; verified target repair supplies {target}.')69return result70