Spaces:
Sleeping
Sleeping
File size: 6,555 Bytes
300df0f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | """
Data models for cross-reference extraction.
All dataclasses are pure Python (no Neo4j dependency) so they can be
used/tested independently by any team member without a running DB.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
# ---------------------------------------------------------------------------
# Enums
# ---------------------------------------------------------------------------
class RefType(str, Enum):
"""Top-level classification of a reference."""
INTERNAL = "INTERNAL" # same document, different Điều/Khoản/Điểm
EXTERNAL = "EXTERNAL" # cross-document reference
MODIFICATION = "MODIFICATION" # sửa đổi / bổ sung / bãi bỏ link
class ModAction(str, Enum):
"""The legal action performed by a modifying document."""
SUA_DOI = "sua_doi" # sửa đổi — replace content
BO_SUNG = "bo_sung" # bổ sung — insert new content
THAY_THE = "thay_the" # thay thế — replace a segment
BAI_BO = "bai_bo" # bãi bỏ — void/remove
HET_HIEU_LUC = "het_hieu_luc" # hết hiệu lực một phần
class DocType(str, Enum):
"""Loại văn bản supported by the system."""
LUAT = "Luật"
BO_LUAT = "Bộ luật"
NGHI_DINH = "Nghị định"
THONG_TU = "Thông tư"
TTLT = "Thông tư liên tịch"
QUYET_DINH = "Quyết định" # out-of-scope but kept for logging
UNKNOWN = "unknown"
# ---------------------------------------------------------------------------
# Reference dataclasses (plain data, no DB coupling)
# ---------------------------------------------------------------------------
@dataclass
class InternalRef:
"""
A reference from one structural element to another within the same document.
Produced by: T2.1 — extract_internal_references()
Written to Neo4j as: [:REFERENCES_INTERNAL]
"""
source_doc_id: str # Document node id
source_article_uid: str # Article.uid of the citing element
source_clause_uid: Optional[str] = None # Clause.uid if reference is inside a clause
source_point_uid: Optional[str] = None # Point.uid if reference is inside a point
# Target within the same document
target_article_index: int = 0 # Điều number (integer)
target_clause_index: Optional[int] = None # Khoản number
target_point_label: Optional[str] = None # Điểm letter, e.g. "a", "b"
# Resolved node UIDs (filled in after lookup; None = unresolved)
target_article_uid: Optional[str] = None
target_clause_uid: Optional[str] = None
target_point_uid: Optional[str] = None
context_text: str = "" # original phrase where the reference was found
confidence: float = 1.0 # 1.0 = exact regex match, <1.0 = fuzzy/ambiguous
start_char: int = 0 # Start position in the original fragment
end_char: int = 0 # End position in the original fragment
is_exception: bool = False # Phase 2: Đánh dấu quan hệ ngoại trừ
@dataclass
class ExternalRef:
"""
A reference from a provision in one document to a provision in another.
Produced by: T2.2 — extract_external_references()
Written to Neo4j as: [:REFERENCES_EXTERNAL]
"""
source_doc_id: str
source_article_uid: str
source_clause_uid: Optional[str] = None
source_point_uid: Optional[str] = None
# Raw parsed fields (before lookup)
raw_so_ky_hieu: str = "" # e.g. "46/2014/NĐ-CP"
normalized_so_ky_hieu: str = "" # e.g. "ND-046-2014"
target_doc_type: DocType = DocType.UNKNOWN
target_article_index: Optional[int] = None
target_clause_index: Optional[int] = None
target_point_label: Optional[str] = None
# Resolved IDs (filled in after lookup)
target_doc_id: Optional[str] = None
target_article_uid: Optional[str] = None
target_clause_uid: Optional[str] = None
target_point_uid: Optional[str] = None
context_text: str = ""
match_method: str = "exact" # "exact" | "fuzzy_levenshtein" | "fuzzy_substring"
confidence: float = 1.0
start_char: int = 0
end_char: int = 0
is_exception: bool = False # Phase 2: Đánh dấu nếu là quan hệ ngoại trừ (trừ trường hợp...)
@dataclass
class ModificationRef:
"""
An article-level modification link produced from a "sửa đổi/bổ sung" document.
Produced by: T2.3 — extract_modification_references()
Written to Neo4j as: [:MODIFIES]
"""
# The modifying document
source_doc_id: str
source_article_uid: str # Article in the modifying doc that contains the action
source_clause_index: Optional[str] = None # Clause number in the source document
# The action
action: ModAction = ModAction.SUA_DOI
# The target (resolved)
raw_target_so_ky_hieu: str = ""
target_doc_id: Optional[str] = None
target_article_index: Optional[int] = None
target_clause_index: Optional[int] = None
target_point_label: Optional[str] = None
target_doc_uid: Optional[str] = None
target_article_uid: Optional[str] = None
target_clause_uid: Optional[str] = None
target_point_uid: Optional[str] = None
new_text: Optional[str] = None # replacement/inserted text (for sửa đổi/bổ sung)
context_text: str = ""
confidence: float = 1.0
start_char: int = 0
end_char: int = 0
# T2.3 improvement: flag to indicate this ref needs target_doc_id/article from context
is_partial_ref: bool = False
# ---------------------------------------------------------------------------
# Aggregate result container
# ---------------------------------------------------------------------------
@dataclass
class ExtractionResult:
"""
Returned by CrossReferenceExtractor for a single document.
All lists contain only successfully *parsed* references
(resolution happens in a separate step).
"""
doc_id: str
internal_refs: list[InternalRef] = field(default_factory=list)
external_refs: list[ExternalRef] = field(default_factory=list)
modification_refs: list[ModificationRef] = field(default_factory=list)
# Counts for monitoring
parse_errors: list[str] = field(default_factory=list) # human-readable error strings
@property
def total(self) -> int:
return len(self.internal_refs) + len(self.external_refs) + len(self.modification_refs)
|