Legora / db /parsers /consitution /explanation_parser.py
sai-Rohan's picture
combined results of all parsers and made a single pipeline
dbabef2
Raw
History Blame Contribute Delete
5.06 kB
from __future__ import annotations
import re
from typing import List
from db.parsers.consitution.legal_models import Explanation
class ExplanationParser:
"""
Constitution / Legal Explanation Parser
Supports:
Explanation.—
Explanation I.—
Explanation II.—
Explanation III.—
Explanation 1.—
Explanation 2.—
"""
# =====================================================
# EXPLANATION PATTERN
# =====================================================
EXPLANATION_RE = re.compile(
r"""
(
Explanation
(?:
\s+
(
[IVXLCDM]+
|
\d+
)
)?
\s*
[:\.\-—]*
.*?
)
(?=
Explanation
(?:
\s+
(
[IVXLCDM]+
|
\d+
)
)?
\s*
[:\.\-—]
|
Illustration
|
Provided
|
$
)
""",
re.IGNORECASE
| re.DOTALL
| re.VERBOSE
)
# =====================================================
# CLEAN TEXT
# =====================================================
def clean_text(
self,
text: str
) -> str:
text = text.strip()
text = re.sub(
r"\s+",
" ",
text
)
return text
# =====================================================
# EXTRACT NUMBER
# =====================================================
def extract_number(
self,
explanation_text: str
) -> str:
m = re.match(
r"""
Explanation
(?:
\s+
(
[IVXLCDM]+
|
\d+
)
)?
""",
explanation_text,
re.IGNORECASE
| re.VERBOSE
)
if not m:
return ""
return (
m.group(1)
if m.group(1)
else ""
)
# =====================================================
# PARSE EXPLANATIONS
# =====================================================
def parse_explanations(
self,
article_text: str
) -> List[Explanation]:
explanations = []
matches = list(
self.EXPLANATION_RE.finditer(
article_text
)
)
for match in matches:
text = self.clean_text(
match.group(1)
)
explanation_no = (
self.extract_number(
text
)
)
explanations.append(
Explanation(
document="constitution",
explanation_no=explanation_no,
text=text
)
)
return explanations
# =====================================================
# VALIDATION
# =====================================================
def validate_explanations(
self,
explanations: List[Explanation]
) -> List[str]:
errors = []
seen = set()
for exp in explanations:
key = (
exp.explanation_no
or "__DEFAULT__"
)
if key in seen:
errors.append(
f"Duplicate "
f"Explanation "
f"{key}"
)
seen.add(key)
if not exp.text:
errors.append(
f"Empty Explanation"
)
if len(exp.text) < 15:
errors.append(
f"Suspiciously Short "
f"Explanation "
f"{key}"
)
return errors
# =========================================================
# EXAMPLE
# =========================================================
if __name__ == "__main__":
sample = """
Some legal provision.
Explanation I.—
For the purposes of this Article,
the term citizen includes...
Explanation II.—
This shall not apply to...
Provided that no person...
"""
parser = ExplanationParser()
explanations = (
parser.parse_explanations(
sample
)
)
print(
f"Found "
f"{len(explanations)} "
f"Explanations"
)
for e in explanations:
print()
print(
"No:",
e.explanation_no
)
print(
e.text
)
print(
parser.validate_explanations(
explanations
)
)