DazedTL/tests/test_csv_skip_translated.py
DazedAnon 65a7ee51c2 feat(csv): add opt-in skip for already-translated batches
- Skip candidate batches when every target is translated
- Keep mixed batches and use global BATCHSIZE for checks
- Add CSV setting checkbox and unit coverage
2026-07-21 16:12:20 -05:00

207 lines
8.4 KiB
Python

import unittest
from unittest.mock import MagicMock, patch
import modules.csv as csv_mod
class TargetTranslatedDetectionTests(unittest.TestCase):
def setUp(self):
self._orig = {
"SOURCE_COLUMN": csv_mod.SOURCE_COLUMN,
"TARGET_COLUMN": csv_mod.TARGET_COLUMN,
"WRITE_TO_NEXT_COLUMN": csv_mod.WRITE_TO_NEXT_COLUMN,
"USE_TARGET_IF_NOT_EMPTY": csv_mod.USE_TARGET_IF_NOT_EMPTY,
"SKIP_HEADER_ROW": csv_mod.SKIP_HEADER_ROW,
"SKIP_COMMENT_ROWS": csv_mod.SKIP_COMMENT_ROWS,
"SKIP_IF_TARGET_TRANSLATED": csv_mod.SKIP_IF_TARGET_TRANSLATED,
"BATCHSIZE": csv_mod.BATCHSIZE,
}
csv_mod.SOURCE_COLUMN = 0
csv_mod.TARGET_COLUMN = 1
csv_mod.WRITE_TO_NEXT_COLUMN = False
csv_mod.USE_TARGET_IF_NOT_EMPTY = False
csv_mod.SKIP_HEADER_ROW = False
csv_mod.SKIP_COMMENT_ROWS = False
def tearDown(self):
for key, value in self._orig.items():
setattr(csv_mod, key, value)
def test_empty_target_not_translated(self):
self.assertFalse(csv_mod._target_is_translated(["こんにちは", ""]))
def test_missing_target_column_not_translated(self):
self.assertFalse(csv_mod._target_is_translated(["こんにちは"]))
def test_english_target_is_translated(self):
self.assertTrue(csv_mod._target_is_translated(["こんにちは", "Hello"]))
def test_japanese_target_not_translated(self):
self.assertFalse(csv_mod._target_is_translated(["こんにちは", "こんにちは"]))
def test_write_to_next_column_checks_next(self):
csv_mod.WRITE_TO_NEXT_COLUMN = True
self.assertFalse(csv_mod._target_is_translated(["こんにちは", "Hello", ""]))
self.assertTrue(csv_mod._target_is_translated(["こんにちは", "Hello", "Hello there"]))
class SkipTranslatedBatchIndexTests(unittest.TestCase):
def setUp(self):
self._orig = {
"SOURCE_COLUMN": csv_mod.SOURCE_COLUMN,
"TARGET_COLUMN": csv_mod.TARGET_COLUMN,
"WRITE_TO_NEXT_COLUMN": csv_mod.WRITE_TO_NEXT_COLUMN,
"USE_TARGET_IF_NOT_EMPTY": csv_mod.USE_TARGET_IF_NOT_EMPTY,
"SKIP_HEADER_ROW": csv_mod.SKIP_HEADER_ROW,
"SKIP_COMMENT_ROWS": csv_mod.SKIP_COMMENT_ROWS,
"SKIP_IF_TARGET_TRANSLATED": csv_mod.SKIP_IF_TARGET_TRANSLATED,
"BATCHSIZE": csv_mod.BATCHSIZE,
}
csv_mod.SOURCE_COLUMN = 0
csv_mod.TARGET_COLUMN = 1
csv_mod.WRITE_TO_NEXT_COLUMN = False
csv_mod.USE_TARGET_IF_NOT_EMPTY = False
csv_mod.SKIP_HEADER_ROW = False
csv_mod.SKIP_COMMENT_ROWS = False
csv_mod.BATCHSIZE = 30
def tearDown(self):
for key, value in self._orig.items():
setattr(csv_mod, key, value)
def test_default_off_keeps_all_candidates(self):
csv_mod.SKIP_IF_TARGET_TRANSLATED = False
data = [
["こんにちは", "Hello"],
["さようなら", ""],
]
self.assertEqual(csv_mod._collect_process_indices(data), [0, 1])
def test_fully_translated_batch_is_skipped(self):
csv_mod.SKIP_IF_TARGET_TRANSLATED = True
csv_mod.BATCHSIZE = 2
data = [
["", "A"],
["", "I"],
["", ""],
["", ""],
]
# First batch (0,1) all translated -> skip; second batch (2,3) needs work
self.assertEqual(csv_mod._collect_process_indices(data), [2, 3])
def test_partial_batch_keeps_entire_batch(self):
csv_mod.SKIP_IF_TARGET_TRANSLATED = True
csv_mod.BATCHSIZE = 3
data = [
["", "A"],
["", ""],
["", "U"],
["", "E"],
["", "O"],
["", "Ka"],
]
# Batch [0,1,2] has untranslated row 1 -> keep all three
# Batch [3,4,5] all translated -> skip
self.assertEqual(csv_mod._collect_process_indices(data), [0, 1, 2])
def test_uses_global_batch_size(self):
csv_mod.SKIP_IF_TARGET_TRANSLATED = True
csv_mod.BATCHSIZE = 30
data = [["" + str(i), f"Line {i}"] for i in range(30)]
data.append(["未翻訳", ""])
# First 30 fully translated -> skipped; last singleton batch needs work
self.assertEqual(csv_mod._collect_process_indices(data), [30])
class SkipIfTargetTranslatedCollectTests(unittest.TestCase):
def setUp(self):
self._orig = {
"SOURCE_COLUMN": csv_mod.SOURCE_COLUMN,
"TARGET_COLUMN": csv_mod.TARGET_COLUMN,
"SPEAKER_COLUMN": csv_mod.SPEAKER_COLUMN,
"SKIP_HEADER_ROW": csv_mod.SKIP_HEADER_ROW,
"USE_TARGET_IF_NOT_EMPTY": csv_mod.USE_TARGET_IF_NOT_EMPTY,
"SKIP_IF_TARGET_TRANSLATED": csv_mod.SKIP_IF_TARGET_TRANSLATED,
"BATCHSIZE": csv_mod.BATCHSIZE,
"WRITE_TO_NEXT_COLUMN": csv_mod.WRITE_TO_NEXT_COLUMN,
"PARSE_NAME_TAGS": csv_mod.PARSE_NAME_TAGS,
"PARSE_M_MARKERS": csv_mod.PARSE_M_MARKERS,
"REMOVE_FURIGANA": csv_mod.REMOVE_FURIGANA,
"SKIP_COMMENT_ROWS": csv_mod.SKIP_COMMENT_ROWS,
"ESTIMATE": csv_mod.ESTIMATE,
}
csv_mod.SOURCE_COLUMN = 0
csv_mod.TARGET_COLUMN = 1
csv_mod.SPEAKER_COLUMN = -1
csv_mod.SKIP_HEADER_ROW = False
csv_mod.USE_TARGET_IF_NOT_EMPTY = False
csv_mod.WRITE_TO_NEXT_COLUMN = False
csv_mod.PARSE_NAME_TAGS = False
csv_mod.PARSE_M_MARKERS = False
csv_mod.REMOVE_FURIGANA = False
csv_mod.SKIP_COMMENT_ROWS = False
csv_mod.ESTIMATE = True
csv_mod.BATCHSIZE = 30
def tearDown(self):
for key, value in self._orig.items():
setattr(csv_mod, key, value)
def test_default_off_does_not_skip_translated_targets(self):
csv_mod.SKIP_IF_TARGET_TRANSLATED = False
data = [
["こんにちは", "Hello"],
["さようなら", ""],
]
pbar = MagicMock()
with patch.object(csv_mod, "translateAI", return_value=(["Hello", "Goodbye"], [0, 0])) as mock_ai:
with patch.object(csv_mod, "dazedwrap") as mock_wrap:
mock_wrap.wrapText.side_effect = lambda text, _width: text
csv_mod.translateCSV(data, pbar, None, MagicMock(), "test.csv", None)
self.assertEqual(mock_ai.call_args[0][0], ["こんにちは", "さようなら"])
def test_opt_in_skips_only_fully_translated_batches(self):
csv_mod.SKIP_IF_TARGET_TRANSLATED = True
csv_mod.BATCHSIZE = 2
data = [
["こんにちは", "Hello"],
["おはよう", "Good morning"],
["さようなら", ""],
["ありがとう", "ありがとう"],
]
pbar = MagicMock()
with patch.object(
csv_mod, "translateAI", return_value=(["Goodbye", "Thank you"], [0, 0])
) as mock_ai:
with patch.object(csv_mod, "dazedwrap") as mock_wrap:
mock_wrap.wrapText.side_effect = lambda text, _width: text
csv_mod.translateCSV(data, pbar, None, MagicMock(), "test.csv", None)
# First batch fully translated -> skipped; second batch kept whole
self.assertEqual(mock_ai.call_args[0][0], ["さようなら", "ありがとう"])
self.assertEqual(data[0][1], "Hello")
self.assertEqual(data[1][1], "Good morning")
self.assertEqual(data[2][1], "Goodbye")
self.assertEqual(data[3][1], "Thank you")
def test_partial_batch_retranslates_already_done_rows(self):
csv_mod.SKIP_IF_TARGET_TRANSLATED = True
csv_mod.BATCHSIZE = 2
data = [
["こんにちは", "Hello"],
["さようなら", ""],
]
pbar = MagicMock()
with patch.object(
csv_mod, "translateAI", return_value=(["Hi again", "Goodbye"], [0, 0])
) as mock_ai:
with patch.object(csv_mod, "dazedwrap") as mock_wrap:
mock_wrap.wrapText.side_effect = lambda text, _width: text
csv_mod.translateCSV(data, pbar, None, MagicMock(), "test.csv", None)
# Mixed batch: keep both rows, including the already-translated one
self.assertEqual(mock_ai.call_args[0][0], ["こんにちは", "さようなら"])
self.assertEqual(data[0][1], "Hi again")
self.assertEqual(data[1][1], "Goodbye")
if __name__ == "__main__":
unittest.main()