| """ |
| Load PDF, extract main text, clean, parse into sections, save sections to disk |
| """ |
|
|
| import json |
| import re |
| import tiktoken |
| from langchain_community.document_loaders import PyPDFLoader |
|
|
| |
| def load_pdf_text(pdf_path): |
| loader = PyPDFLoader(pdf_path) |
| pages = loader.load() |
| full_text = "\n".join([p.page_content for p in pages]) |
| return full_text |
|
|
| |
| def extract_main_text(raw_text): |
| |
| match = re.search( |
| r'DO\s+HEREBY\s+MAKE.*?following\s+Constitution:\s*', |
| raw_text, |
| re.DOTALL | re.IGNORECASE |
| ) |
| if match: |
| start_idx = match.end() |
| return raw_text[start_idx:].strip() |
| else: |
| |
| first = raw_text.find("CHAPTER I") |
| if first != -1: |
| second = raw_text.find("CHAPTER I", first + 10) |
| if second != -1: |
| return raw_text[second:].strip() |
| raise ValueError("Cannot locate real constitutional text.") |
|
|
| |
| def clean_constitution(text): |
| |
| text = re.sub( |
| r'\n?\d*\n?The Constitution of the Federal Republic of Nigeria Updated with the First, Second, Third, Fourth and Fifth Alterations\n', |
| '\n', |
| text |
| ) |
| |
| text = re.sub(r'\n\d+\n', '\n', text) |
| |
| text = re.sub(r'\n{3,}', '\n\n', text) |
| return text.strip() |
|
|
| |
| def parse_constitution_sections(clean_text): |
| |
| text = "\n" + clean_text |
| pattern = r'\n(?P<num>\d+)\.\s+(?P<title>[^\n]+)\n(?P<body>.*?)(?=\n\d+\.\s+[A-Z]|\Z)' |
| matches = list(re.finditer(pattern, text, re.DOTALL)) |
| sections = [] |
| for m in matches: |
| num = m.group('num') |
| title = m.group('title') |
| body = m.group('body').strip() |
| sections.append({ |
| 'source': 'Constitution', |
| 'section_number': num, |
| 'title': title, |
| 'content': body |
| }) |
| return sections |
|
|
| |
| def filter_valid_sections(sections): |
| valid = [] |
| seen = set() |
| for s in sections: |
| num = s['section_number'] |
| |
| if num in seen: |
| continue |
| |
| try: |
| int(num) |
| if 1 <= int(num) <= 320: |
| valid.append(s) |
| seen.add(num) |
| continue |
| except ValueError: |
| pass |
| |
| if num in ['16A', '225A', '254A', '254B', '254C', '254D', '254E', '254F']: |
| valid.append(s) |
| seen.add(num) |
| return valid |
|
|
|
|
|
|
| |
| if __name__ == "__main__": |
| pdf_path = "data/raw/Constitution.pdf" |
| raw_text = load_pdf_text(pdf_path) |
| print("Step 1 – Raw text loaded (first 500 chars):") |
| |
| print("\n" + "="*80 + "\n") |
|
|
| main_text = extract_main_text(raw_text) |
| print("Step 2 – After extracting main text (first 1000 chars):") |
| print(main_text[:1000]) |
| print("\n" + "="*80 + "\n") |
|
|
| cleaned = clean_constitution(main_text) |
| print("Step 3 – After cleaning headers/page numbers (first 1000 chars):") |
| print(cleaned[:1000]) |
| print("\n" + "="*80 + "\n") |
|
|
| sections = parse_constitution_sections(cleaned) |
| sections = filter_valid_sections(sections) |
| print(f"Step 4 – Found {len(sections)} sections.") |
| |
| for sec in sections[:10]: |
| print(f"Section {sec['section_number']}: {sec['title']}") |
| print("...") |
| |
| sec37 = [s for s in sections if s['section_number'] == '37'] |
| if sec37: |
| print("Section 37 found:", sec37[0]['title']) |
| else: |
| print("WARNING: Section 37 NOT FOUND – check regex or text.") |
| print("\n" + "="*80 + "\n") |
|
|
| |
| for s in sections[:3]: |
| print(f"Section {s['section_number']}: {s['content'][:200]}...") |
| print("\n" + "="*80 + "\n") |
|
|
| |
| for s in sections: |
| if int(s['section_number']) in range(33, 47): |
| print(s['section_number'], s['title']) |
| print("\n" + "="*80 + "\n") |
|
|
| |
| with open("data/cleaned/constitution_sections.json", "w", encoding="utf-8") as f: |
| json.dump(sections, f, indent=2) |
| print(f"Saved {len(sections)} sections to data/cleaned/constitution_sections.json") |