Files
epub_bilingual_translator/scripts/build_sample.py
T

48 lines
2.3 KiB
Python

"""Apply an agent-authored translation manifest for offline, reproducible QA."""
import argparse
import asyncio
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from src.common.data_model import BookStructure, ManifestEntry
from src.preprocessing.epub_cleaner import EpubCleaner
from src.preprocessing.text_extractor import FineGrainedExtractor
from src.preprocessing.navigation import prepare_navigation
from src.assembly.restoration_engine import RestorationEngine
from src.assembly.backfiller import BackfillEngine
from src.assembly.builder import BilingualBuilder
async def run(args):
args.out.mkdir(parents=True,exist_ok=True)
structure=BookStructure.load(EpubCleaner(args.source,args.out/'source').clean())
entries=FineGrainedExtractor().extract(structure)
prepare_navigation(args.source,structure,entries)
by_id={e.entry_id:e for e in entries}
provided=[ManifestEntry.model_validate(e) for e in json.loads(args.manifest.read_text())]
for sample in provided:
current=by_id.get(sample.entry_id)
if current is None or current.original_text!=sample.original_text or current.placeholders!=sample.placeholders:
raise ValueError(f'Sample/source mismatch: {sample.entry_id}')
current.translated_text=sample.translated_text
count=await RestorationEngine().restore_entries(entries)
if count!=len(provided):
raise ValueError('Sample has missing or invalid translations')
(args.out/'manifest.json').write_text(json.dumps([e.model_dump() for e in entries],ensure_ascii=False,indent=2))
for mode in ['bilingual','target_only']:
copy=structure.model_copy(deep=True)
await BackfillEngine().backfill(copy,entries,mode,allow_partial=True)
BilingualBuilder(args.out/'source',args.source).build(copy,args.out/f'{mode}-sample.epub')
print(f'Explicit partial sample: {count}/{len(entries)} translated units. Outputs: {args.out}')
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('source',type=Path)
parser.add_argument('manifest',type=Path)
parser.add_argument('--out',type=Path,default=Path('.work/offline-sample'))
asyncio.run(run(parser.parse_args()))
if __name__=='__main__': main()