Legora / db /parsers /bsa /section_parser.py
sai-Rohan's picture
combined results of all parsers and made a single pipeline
dbabef2
Raw
History Blame Contribute Delete
5.93 kB
from __future__ import annotations
import re
from typing import List
from db.parsers.bsa.cleaner import BSATextCleaner
from db.parsers.bsa.legal_models import Section
class SectionParser:
"""
Bharatiya Sakshya Adhiniyam Section Parser
"""
# =====================================================
# SECTION HEADER
# =====================================================
SECTION_RE = re.compile(
r"(?m)^(\d{1,3})\.\s+(.+)$"
)
SECTION_NO_RE = re.compile(
r"^\d+$"
)
# =====================================================
# CLEAN TITLE
# =====================================================
@staticmethod
def clean_title(
title: str
) -> str:
title = title.strip()
title = re.sub(
r"\s+",
" ",
title
)
title = title.strip(
".-— "
)
return title
# =====================================================
# RECOVER TITLE
# =====================================================
def recover_title(
self,
section_text: str,
current_title: str
) -> str:
if current_title:
return self.clean_title(
current_title
)
lines = [
x.strip()
for x in section_text.split("\n")
if x.strip()
]
if len(lines) < 2:
return ""
return self.clean_title(
lines[1]
)
# =====================================================
# REMOVE STATEMENT OF OBJECTS
# =====================================================
@staticmethod
def remove_statement_of_objects(
text: str
) -> str:
marker = (
"The Commission suggested various enactments"
)
idx = text.find(
marker
)
if idx != -1:
text = text[:idx]
return text
# =====================================================
# EXTRACT SECTIONS
# =====================================================
def extract_sections(
self,
text: str
) -> List[Section]:
text = (
self.remove_statement_of_objects(
text
)
)
matches = list(
self.SECTION_RE.finditer(
text
)
)
if not matches:
return []
sections = []
for i, match in enumerate(
matches
):
start = match.start()
end = (
matches[i + 1].start()
if i + 1 < len(matches)
else len(text)
)
section_no = (
match.group(1)
.strip()
)
section_title = (
match.group(2)
.strip()
)
section_text = (
text[start:end]
.strip()
)
section_title = (
self.recover_title(
section_text,
section_title
)
)
sections.append(
Section(
document="bsa",
section_no=
section_no,
title=
section_title,
text=
section_text
)
)
return sections
# =====================================================
# VALIDATION
# =====================================================
def validate_sections(
self,
sections: List[Section]
) -> List[str]:
errors = []
seen = set()
for section in sections:
section_no = (
section.section_no
)
if section_no in seen:
errors.append(
f"Duplicate Section "
f"{section_no}"
)
seen.add(
section_no
)
if not self.SECTION_NO_RE.match(
section_no
):
errors.append(
f"Invalid Section "
f"{section_no}"
)
if not section.title:
errors.append(
f"Missing title for "
f"Section {section_no}"
)
return errors
# =====================================================
# GET SECTION
# =====================================================
def get_section(
self,
sections: List[Section],
section_no: str
):
for section in sections:
if (
section.section_no
== section_no
):
return section
return None
# =========================================================
# TEST
# =========================================================
if __name__ == "__main__":
with open(
"../../pdfs/bsa.txt",
"r",
encoding="utf8"
) as f:
text = f.read()
cleaner = BSATextCleaner()
text = cleaner.clean(
text
)
parser = SectionParser()
sections = (
parser.extract_sections(
text
)
)
print(
f"Sections Found: "
f"{len(sections)}"
)
print()
for section in sections[:20]:
print(
f"{section.section_no}"
f" -> "
f"{section.title}"
)
print()
errors = (
parser.validate_sections(
sections
)
)
print(
f"Errors: "
f"{len(errors)}"
)
for error in errors:
print(error)