feat(numbering): publish canonical numbering API
Add a public numbering module and route fragmenting, validation, and scaffold preparation through the canonical numbering entry. Rewrite the repository entry docs around the fixed numbering contract, add MkDocs landing pages, and document the mirror mapping used for medicinal-chemistry comparisons. Also refresh the validation analysis reports to explain the canonical-versus-mirrored numbering relationship.
This commit is contained in:
34
tests/test_documentation_entrypoints.py
Normal file
34
tests/test_documentation_entrypoints.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_root_readme_documents_canonical_numbering() -> None:
|
||||
readme = (PROJECT_ROOT / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
assert "1 = 内酯羰基碳" in readme
|
||||
assert "2 = 相邻酯氧" in readme
|
||||
assert "3..N = 从 2 位出发沿环唯一图遍历顺序继续编号" in readme
|
||||
assert "6 → 13" in readme
|
||||
assert "7 → 12" in readme
|
||||
|
||||
|
||||
def test_root_agents_exists_and_documents_numbering_invariants() -> None:
|
||||
agents_path = PROJECT_ROOT / "AGENTS.md"
|
||||
|
||||
assert agents_path.exists()
|
||||
agents = agents_path.read_text(encoding="utf-8")
|
||||
assert "canonical numbering" in agents
|
||||
assert "不是视觉顺时针" in agents
|
||||
assert "bridge / fused multi-anchor" in agents
|
||||
|
||||
|
||||
def test_mkdocs_ring_numbering_page_documents_mirror_mapping() -> None:
|
||||
ring_doc = (PROJECT_ROOT / "docs" / "user-guide" / "ring-numbering.md").read_text(encoding="utf-8")
|
||||
|
||||
assert "p_mirror = ring_size - p + 3" in ring_doc
|
||||
assert "6 → 13" in ring_doc
|
||||
assert "15 → 4" in ring_doc
|
||||
52
tests/test_numbering_api.py
Normal file
52
tests/test_numbering_api.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from macro_lactone_toolkit import (
|
||||
MacrolactoneFragmenter,
|
||||
mirror_macrolactone_position,
|
||||
mirror_macrolactone_positions,
|
||||
number_macrolactone,
|
||||
)
|
||||
from macro_lactone_toolkit.splicing.scaffold_prep import prepare_macrolactone_scaffold
|
||||
|
||||
from .helpers import build_macrolactone
|
||||
|
||||
|
||||
def test_number_macrolactone_matches_fragmenter_numbering() -> None:
|
||||
built = build_macrolactone(16, {5: "methyl"})
|
||||
|
||||
api_result = number_macrolactone(built.smiles, ring_size=16)
|
||||
fragmenter_result = MacrolactoneFragmenter(ring_size=16).number_molecule(built.smiles)
|
||||
|
||||
assert api_result.position_to_atom == fragmenter_result.position_to_atom
|
||||
assert api_result.atom_to_position == fragmenter_result.atom_to_position
|
||||
|
||||
|
||||
def test_mirror_macrolactone_position_for_ring16() -> None:
|
||||
assert mirror_macrolactone_position(6, 16) == 13
|
||||
assert mirror_macrolactone_position(7, 16) == 12
|
||||
assert mirror_macrolactone_position(15, 16) == 4
|
||||
assert mirror_macrolactone_position(16, 16) == 3
|
||||
|
||||
|
||||
def test_mirror_macrolactone_positions_returns_stable_mapping() -> None:
|
||||
assert mirror_macrolactone_positions([6, 7, 15, 16], 16) == {
|
||||
6: 13,
|
||||
7: 12,
|
||||
15: 4,
|
||||
16: 3,
|
||||
}
|
||||
|
||||
|
||||
def test_prepare_scaffold_keeps_requested_position_label() -> None:
|
||||
built = build_macrolactone(16, {5: "ethyl"})
|
||||
|
||||
scaffold, dummy_map = prepare_macrolactone_scaffold(
|
||||
built.mol,
|
||||
positions=[5],
|
||||
ring_size=16,
|
||||
)
|
||||
|
||||
numbering = number_macrolactone(built.mol, ring_size=16)
|
||||
assert 5 in dummy_map
|
||||
assert numbering.position_to_atom[5] == built.position_to_atom[5]
|
||||
assert numbering.position_to_atom[5] == scaffold.GetAtomWithIdx(dummy_map[5]).GetNeighbors()[0].GetIdx()
|
||||
@@ -285,6 +285,10 @@ def test_analyze_validation_fragment_library_script_generates_reports(tmp_path):
|
||||
report_zh = (output_dir / "fragment_library_analysis_report_zh.md").read_text(encoding="utf-8")
|
||||
assert "桥连或双锚点侧链不会进入当前片段库" in report_zh
|
||||
assert "cyclic single-anchor side chains" in report_zh
|
||||
assert "6 → 13" in report_zh
|
||||
assert "7 → 12" in report_zh
|
||||
assert "15 → 4" in report_zh
|
||||
assert "16 → 3" in report_zh
|
||||
|
||||
|
||||
def test_active_text_assets_do_not_reference_legacy_api():
|
||||
|
||||
Reference in New Issue
Block a user