Improve EPUB restoration, book terminology and Calibre QA workflow
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"""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.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)
|
||||
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()
|
||||
Reference in New Issue
Block a user