generated from coulomb/repo-seed
feat: WP-0003 complete — LEVEL3 advanced features + error framework
Implements full LEVEL3 feature set: cross-references (xref.py), numbered figures (figures.py), auto-diagrams (diagrams.py), bibliography/citations (bibliography.py), LEVEL3 capability detection (level3.py), and structured error/warning records (errors.py). Builder, importer, and differ updated for LEVEL3 round-trip support. REST and MCP interfaces updated with structured warning records. 259 tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ from __future__ import annotations
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from markidocx.errors import OutputState
|
||||
|
||||
HEADING_RE = re.compile(r"^(#{1,6})\s+(.+)$", re.MULTILINE)
|
||||
LIST_ITEM_RE = re.compile(r"^(\s*[-*+]|\s*\d+\.)\s+(.+)$", re.MULTILINE)
|
||||
TABLE_ROW_RE = re.compile(r"^\|.+\|$", re.MULTILINE)
|
||||
@@ -19,6 +21,7 @@ class DriftReport:
|
||||
degraded: list[str] = field(default_factory=list)
|
||||
broken: list[str] = field(default_factory=list)
|
||||
unsupported: list[str] = field(default_factory=list)
|
||||
output_state: OutputState = OutputState.FINAL
|
||||
|
||||
|
||||
def compare(original: str, reimported: str) -> DriftReport:
|
||||
@@ -76,13 +79,29 @@ def compare(original: str, reimported: str) -> DriftReport:
|
||||
else:
|
||||
degraded.append(f"link:lost {link[:40]}")
|
||||
|
||||
# --- Cross-references (FR-531, FR-540) ---
|
||||
_compare_xrefs(original, reimported, preserved, degraded, broken)
|
||||
|
||||
# --- Figures (FR-532, FR-541) ---
|
||||
_compare_figures(original, reimported, preserved, degraded, broken)
|
||||
|
||||
# --- Citations & Bibliography (FR-535, FR-542) ---
|
||||
from markidocx.bibliography import compare_citations
|
||||
|
||||
compare_citations(original, reimported, preserved, degraded, broken)
|
||||
|
||||
has_drift = bool(degraded or broken)
|
||||
output_state = (
|
||||
OutputState.FINAL if not has_drift
|
||||
else (OutputState.DEGRADED if not broken else OutputState.PARTIAL)
|
||||
)
|
||||
return DriftReport(
|
||||
has_drift=has_drift,
|
||||
preserved=preserved,
|
||||
degraded=degraded,
|
||||
broken=broken,
|
||||
unsupported=unsupported,
|
||||
output_state=output_state,
|
||||
)
|
||||
|
||||
|
||||
@@ -104,6 +123,64 @@ def _count_tables(text: str) -> int:
|
||||
return count
|
||||
|
||||
|
||||
def _compare_figures(
|
||||
original: str,
|
||||
reimported: str,
|
||||
preserved: list[str],
|
||||
degraded: list[str],
|
||||
broken: list[str],
|
||||
) -> None:
|
||||
"""Compare figure labels and captions (FR-532, FR-541)."""
|
||||
from markidocx.figures import extract_figure_captions, extract_figure_labels
|
||||
|
||||
orig_labels = extract_figure_labels(original)
|
||||
reim_labels = extract_figure_labels(reimported)
|
||||
for label in orig_labels:
|
||||
if label in reim_labels:
|
||||
preserved.append(f"figure-label:{label}")
|
||||
else:
|
||||
broken.append(f"figure-label:missing '{label}'")
|
||||
|
||||
orig_captions = extract_figure_captions(original)
|
||||
reim_captions = extract_figure_captions(reimported)
|
||||
orig_set = set(orig_captions)
|
||||
reim_set = set(reim_captions)
|
||||
for caption in orig_set:
|
||||
if caption in reim_set:
|
||||
preserved.append(f"figure-caption:{caption[:40]}")
|
||||
else:
|
||||
degraded.append(f"figure-caption:lost '{caption[:40]}'")
|
||||
|
||||
|
||||
def _compare_xrefs(
|
||||
original: str,
|
||||
reimported: str,
|
||||
preserved: list[str],
|
||||
degraded: list[str],
|
||||
broken: list[str],
|
||||
) -> None:
|
||||
"""Compare cross-reference anchors and links (FR-531, FR-540)."""
|
||||
from markidocx.xref import extract_anchors, extract_xref_links
|
||||
|
||||
orig_anchors = extract_anchors(original)
|
||||
reim_anchors = extract_anchors(reimported)
|
||||
for anchor in orig_anchors:
|
||||
if anchor in reim_anchors:
|
||||
preserved.append(f"xref-anchor:{anchor}")
|
||||
else:
|
||||
broken.append(f"xref-anchor:missing '{anchor}'")
|
||||
|
||||
orig_xrefs = extract_xref_links(original)
|
||||
reim_xrefs = extract_xref_links(reimported)
|
||||
for link_text, anchor in orig_xrefs:
|
||||
if (link_text, anchor) in reim_xrefs:
|
||||
preserved.append(f"xref-link:[{link_text}][{anchor}]")
|
||||
elif anchor not in reim_anchors:
|
||||
broken.append(f"xref-link:broken-target [{link_text}][{anchor}]")
|
||||
else:
|
||||
degraded.append(f"xref-link:degraded [{link_text}][{anchor}]")
|
||||
|
||||
|
||||
def _compare_sets(
|
||||
kind: str,
|
||||
orig: list[str],
|
||||
|
||||
Reference in New Issue
Block a user