fix(search): review ambiguous topic assignments
This commit is contained in:
@@ -5,10 +5,13 @@ import unittest
|
|||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from tools.build_search_topic_taxonomy import build
|
from tools.build_search_topic_taxonomy import build, group_for
|
||||||
|
|
||||||
|
|
||||||
class SearchTopicTaxonomyTest(unittest.TestCase):
|
class SearchTopicTaxonomyTest(unittest.TestCase):
|
||||||
|
def test_ambiguous_roots_are_left_for_manual_review(self):
|
||||||
|
self.assertEqual(group_for("Иностранные инвестиции"), "review_required")
|
||||||
|
|
||||||
def test_preserves_source_tree_counts_and_marks_unclassified_roots(self):
|
def test_preserves_source_tree_counts_and_marks_unclassified_roots(self):
|
||||||
with tempfile.TemporaryDirectory() as temporary:
|
with tempfile.TemporaryDirectory() as temporary:
|
||||||
root = Path(temporary)
|
root = Path(temporary)
|
||||||
@@ -51,6 +54,18 @@ class SearchTopicTaxonomyTest(unittest.TestCase):
|
|||||||
self.assertEqual(groups["review_required"]["children"][0]["grouping_status"], "manual_review")
|
self.assertEqual(groups["review_required"]["children"][0]["grouping_status"], "manual_review")
|
||||||
self.assertEqual(catalog["nodes_missing_kyrgyz_label"], 2)
|
self.assertEqual(catalog["nodes_missing_kyrgyz_label"], 2)
|
||||||
|
|
||||||
|
def test_rebuild_fails_instead_of_omitting_successful_manifest_document(self):
|
||||||
|
with tempfile.TemporaryDirectory() as temporary:
|
||||||
|
root = Path(temporary)
|
||||||
|
(root / "documents/1").mkdir(parents=True)
|
||||||
|
with closing(sqlite3.connect(root / "manifest.sqlite3")) as db:
|
||||||
|
db.execute("CREATE TABLE documents (code TEXT, state TEXT)")
|
||||||
|
db.execute("INSERT INTO documents VALUES ('1', 'success')")
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "normalized document 1"):
|
||||||
|
build(root)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -106,10 +106,10 @@
|
|||||||
отношения; общие и межотраслевые вопросы.
|
отношения; общие и межотраслевые вопросы.
|
||||||
|
|
||||||
Каталог является черновиком: группа корневой рубрики предложена по русской
|
Каталог является черновиком: группа корневой рубрики предложена по русской
|
||||||
подписи, неоднозначные и нераспознанные рубрики помещены в
|
подписи, рубрики с несколькими совпадениями правил или без совпадений помещены
|
||||||
`review_required` и отмечены `manual_review`; остальные помечены
|
в `review_required` и отмечены `manual_review`; остальные помечены
|
||||||
`provisional_lexical`. В корпусе 2 065 узлов и 687 вариантов корневых рубрик;
|
`provisional_lexical`. В корпусе 2 065 узлов и 687 вариантов корневых рубрик;
|
||||||
251 корневой вариант ожидает ручной классификации. Отсутствующие в источнике
|
328 корневых вариантов ожидают ручной классификации. Отсутствующие в источнике
|
||||||
кыргызские подписи оставлены пустыми: они отсутствуют у 1 524 узлов, новые
|
кыргызские подписи оставлены пустыми: они отсутствуют у 1 524 узлов, новые
|
||||||
переводы не придуманы. Внутренние `source-*` ID являются
|
переводы не придуманы. Внутренние `source-*` ID являются
|
||||||
временными хешами кода источника и цепочки подписей; при `Code: null` они
|
временными хешами кода источника и цепочки подписей; при `Code: null` они
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,7 @@ def clean(value):
|
|||||||
def group_for(label):
|
def group_for(label):
|
||||||
text = (label or "").casefold()
|
text = (label or "").casefold()
|
||||||
matches = [code for code, pattern in RULES if re.search(pattern, text)]
|
matches = [code for code, pattern in RULES if re.search(pattern, text)]
|
||||||
return matches[0] if matches else "review_required"
|
return matches[0] if len(matches) == 1 else "review_required"
|
||||||
|
|
||||||
|
|
||||||
def node_id(path):
|
def node_id(path):
|
||||||
@@ -121,8 +121,10 @@ def build(normalized_root):
|
|||||||
path = source_root / code / "document.json"
|
path = source_root / code / "document.json"
|
||||||
try:
|
try:
|
||||||
data = json.loads(path.read_text(encoding="utf-8"))
|
data = json.loads(path.read_text(encoding="utf-8"))
|
||||||
except (OSError, json.JSONDecodeError):
|
except (OSError, json.JSONDecodeError) as error:
|
||||||
return code, []
|
raise RuntimeError(f"cannot read normalized document {code}: {error}") from error
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise RuntimeError(f"normalized document {code} must contain a JSON object")
|
||||||
return code, data.get("general_classifiers", [])
|
return code, data.get("general_classifiers", [])
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=24) as pool:
|
with ThreadPoolExecutor(max_workers=24) as pool:
|
||||||
|
|||||||
Reference in New Issue
Block a user