Files

173 lines
6.9 KiB
Python

from __future__ import annotations
import importlib.util
import json
from pathlib import Path
import tempfile
import unittest
SCRIPT_DIR = Path(__file__).resolve().parents[1] / "scripts"
def load(name: str, filename: str):
spec = importlib.util.spec_from_file_location(name, SCRIPT_DIR / filename)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
pipeline = load("review_test_pipeline", "subtitle_pipeline.py")
review = load("scientific_review", "scientific_review.py")
class ScientificReviewTests(unittest.TestCase):
def setUp(self) -> None:
self.temporary = tempfile.TemporaryDirectory()
self.root = Path(self.temporary.name)
source = self.root / "source.srt"
source.write_text(
"1\n00:00:00,000 --> 00:00:01,000\nInject 50 microliters.\n\n"
"2\n00:00:01,100 --> 00:00:02,000\nExpose the superior sclera.\n\n"
"3\n00:00:02,100 --> 00:00:03,000\nAAV8-GFP expression was detected.\n",
encoding="utf-8",
)
self.manifest_path = pipeline.prepare(source, self.root / "subtitles", "en")
self.manifest = json.loads(self.manifest_path.read_text(encoding="utf-8"))
self.translations = self.root / "translations"
self.translations.mkdir()
records = [
{"id": self.manifest["segments"][0]["id"], "translation": "注射50微升"},
{"id": self.manifest["segments"][1]["id"], "translation": "暴露上方巩膜组织"},
{"id": self.manifest["segments"][2]["id"], "translation": "检测到AAV8-GFP表达"},
]
(self.translations / "batch-0001.json").write_text(
json.dumps({"translations": records}, ensure_ascii=False), encoding="utf-8"
)
self.review_dir = self.root / "subtitles" / "scientific-review"
def tearDown(self) -> None:
self.temporary.cleanup()
def write_profile(self) -> None:
self.review_dir.mkdir(parents=True, exist_ok=True)
(self.review_dir / review.PROFILE_NAME).write_text(
json.dumps(
{
"domains": [
{
"id": "experimental-animal-science",
"label": "实验动物学",
"relevance": "primary",
}
],
"review_focus": ["剂量、解剖方向和载体名称"],
"terminology": [
{
"source": "superior sclera",
"preferred": "上方巩膜",
"category": "anatomy",
"note": "保持方向信息",
}
],
},
ensure_ascii=False,
),
encoding="utf-8",
)
def test_profile_review_finalize_and_bound_render(self) -> None:
first = review.next_batch(self.manifest_path, self.translations, self.review_dir)
self.assertEqual(first["stage"], "domain_profile_required")
self.assertLessEqual(len(first["profile"]["representative_samples"]), 18)
self.write_profile()
pending = review.next_batch(self.manifest_path, self.translations, self.review_dir)
self.assertEqual(pending["stage"], "scientific_review_required")
items = pending["batch"]["items"]
records = [
{
"id": items[0]["id"],
"status": "approved",
"translation": items[0]["translation"],
"severity": "none",
"category": "none",
"reason": "",
},
{
"id": items[1]["id"],
"status": "corrected",
"translation": "暴露上方巩膜",
"severity": "low",
"category": "anatomy",
"reason": "删除原文没有的“组织”",
},
{
"id": items[2]["id"],
"status": "flagged",
"translation": items[2]["translation"],
"severity": "high",
"category": "gene_protein_vector",
"reason": "载体名称需保守保留,无法仅凭字幕确认",
},
]
Path(pending["output_path"]).write_text(
json.dumps({"reviews": records}, ensure_ascii=False), encoding="utf-8"
)
complete = review.next_batch(self.manifest_path, self.translations, self.review_dir)
self.assertTrue(complete["done"])
finalized = review.finalize(self.manifest_path, self.translations, self.review_dir)
self.assertEqual(finalized["counts"]["corrected"], 1)
self.assertEqual(finalized["counts"]["unresolved_high"], 1)
validation_path = pipeline.render(
self.manifest_path,
Path(finalized["reviewed_translations_dir"]),
self.root / "rendered",
scientific_review_report=Path(finalized["report"]),
)
validation = json.loads(validation_path.read_text(encoding="utf-8"))
self.assertTrue(validation["translation_quality_reviewed"])
self.assertFalse(validation["scientific_review"]["human_expert_reviewed"])
target = (self.root / "rendered" / "zh-CN.srt").read_text(encoding="utf-8")
self.assertIn("暴露上方巩膜", target)
self.assertNotIn("暴露上方巩膜组织", target)
self.assertNotIn(items[2]["translation"], target)
def test_correction_cannot_change_protected_number_or_scientific_name(self) -> None:
self.write_profile()
pending = review.next_batch(self.manifest_path, self.translations, self.review_dir)
items = pending["batch"]["items"]
records = []
for item in items:
translation = item["translation"]
status = "approved"
severity = "none"
category = "none"
reason = ""
if "50" in translation:
translation = translation.replace("50", "500")
status = "corrected"
severity = "high"
category = "number_unit"
reason = "unsafe numerical rewrite"
records.append(
{
"id": item["id"],
"status": status,
"translation": translation,
"severity": severity,
"category": category,
"reason": reason,
}
)
output = Path(pending["output_path"])
output.write_text(json.dumps({"reviews": records}, ensure_ascii=False), encoding="utf-8")
with self.assertRaisesRegex(review.ReviewError, "protected numbers"):
review.next_batch(self.manifest_path, self.translations, self.review_dir)
if __name__ == "__main__":
unittest.main()