Work+telugu+family+dengudu+kathalu+pdf+56+better

Madhavi placed the paper on Ari’s desk. It read:

“Once a weaver named Rangam wove a blanket for the king. He wanted it finished in one night, but the threads kept breaking. In his frustration, he abandoned the loom. The next morning, he found that the broken threads had formed a beautiful pattern on the floor. He realized that patience and letting go of control gave him a masterpiece.” work+telugu+family+dengudu+kathalu+pdf+56+better

Ari read it out loud, his voice softening the office’s sterile echo. The story reminded him of his great‑grandfather’s ledger—a reminder that rigidity can break the threads of life, but flexibility weaves new patterns. Madhavi placed the paper on Ari’s desk


#!/usr/bin/env python3
"""
pdf_finder.py
--------------
A tiny, zero‑dependency (except for PyPDF2 & tqdm) CLI tool that:
* Recursively scans a folder for *.pdf files.
* Extracts basic metadata (title, author) from each PDF.
* Builds an in‑memory index that maps keywords → file paths.
* Lets you search that index with free‑text queries.
* Optionally opens the selected PDF using the default OS viewer.
Usage
-----
    python pdf_finder.py /path/to/folder
The script is safe for personal collections – it never uploads anything
outside your machine.
"""
import argparse
import os
import sys
import subprocess
import platform
from pathlib import Path
from typing import List, Dict, Tuple
from tqdm import tqdm
from PyPDF2 import PdfReader
# ----------------------------------------------------------------------
# Helper utilities
# ----------------------------------------------------------------------
def extract_metadata(pdf_path: Path) -> Tuple[str, str]:
    """
    Return (title, author) strings from a PDF's metadata.
    If a field is missing, return an empty string.
    """
    try:
        reader = PdfReader(str(pdf_path))
        info = reader.metadata  # type: ignore[attr-defined]  # PyPDF2 3.x
        title = info.title if info.title else ""
        author = info.author if info.author else ""
        return title, author
    except Exception as e:
        # Corrupt PDFs, encrypted PDFs, etc. – just ignore metadata.
        return "", ""
def normalise(text: str) -> List[str]:
    """
    Lower‑case, strip punctuation, split on whitespace.
    Returns a list of individual words.
    """
    import re
    # Keep only alphanumerics and Telugu Unicode range (U+0C00‑U+0C7F)
    clean = re.sub(r"[^\w\u0C00-\u0C7F]+", " ", text.lower())
    return clean.split()
def build_index(root_dir: Path) -> List[Dict]:
    """
    Walk the directory tree, collect PDF paths + metadata,
    and return a list of dicts like:
"path": Path,
            "filename_words": [...],
            "title_words": [...],
            "author_words": [...]
"""
    pdf_entries = []
# Walk the tree once, gathering PDFs
    for pdf_path in tqdm(list(root_dir.rglob("*.pdf")), desc="Scanning PDFs"):
        title, author = extract_metadata(pdf_path)
entry = 
            "path": pdf_path,
            "filename_words": normalise(pdf_path.stem),
            "title_words": normalise(title),
            "author_words": normalise(author),
pdf_entries.append(entry)
return pdf_entries
def matches(entry: Dict, query_words: List[str]) -> bool:
    """
    Return True if *all* query_words appear in any of the three word lists
    (filename, title, author).  The match is AND‑based across the whole query.
    """
    all_words = set(entry["filename_words"] + entry["title_words"] + entry["author_words"])
    return all(word in all_words for word in query_words)
def open_file(filepath: Path):
    """
    Open the file with the OS‑default PDF viewer.
    Works on Windows, macOS, and most Linux distros.
    """
    system = platform.system()
    try:
        if system == "Windows":
            os.startfile(str(filepath))
        elif system == "Darwin":  # macOS
            subprocess.run(["open", str(filepath)], check=False)
        else:  # Linux and others
            subprocess.run(["xdg-open", str(filepath)], check=False)
    except Exception as e:
        print(f"⚠️ Could not open file: e")
# ----------------------------------------------------------------------
# Main interactive loop
# ----------------------------------------------------------------------
def interactive_search(pdf_index: List[Dict]):
    print("\n🔎  PDF Finder – type keywords to search, or just press ENTER to quit.")
    while True:
        user_input = input("\nEnter search terms (or press ENTER to quit): ").strip()
        if not user_input:
            print("👋 Bye!")
            break
query_words = normalise(user_input)
# Find matches
        matches_list = [e for e in pdf_index if matches(e, query_words)]
if not matches_list:
            print("❌ No PDFs matched your query.")
            continue
# Show a numbered list (limit to 20 results for readability)
        print(f"\n✅ Found len(matches_list) match(es):")
        for i, entry in enumerate(matches_list[:20], start=1):
            title = " | ".join(filter(None, entry["title_words"]))
            author = " | ".join(filter(None, entry["author_words"]))
            meta = f" – Title: title" if title else ""
            meta += f", Author: author" if author else ""
            print(f"i:2. entry['path'].namemeta")
# Ask if the user wants to open one of them
        while True:
            choice = input("\nEnter a result number to open, or press ENTER to search again: ").strip()
            if not choice:
                break
            if not choice.isdigit():
                print("❗ Please type a number or press ENTER.")
                continue
            idx = int(choice) - 1
            if idx < 0 or idx >= len(matches_list[:20]):
                print("❗ Number out of range.")
                continue
chosen_path = matches_list[idx]["path"]
            print(f"📂 Opening: chosen_path")
            open_file(chosen_path)
            break  # after opening, go back to the main query prompt
def main():
    parser = argparse.ArgumentParser(
        description="Search through a local collection of PDF files (e.g. Telugu family stories)."
    )
    parser.add_argument(
        "folder",
        type=str,
        help="Root folder that contains PDFs (will be scanned recursively).",
    )
    args = parser.parse_args()
root = Path(args.folder).expanduser().resolve()
    if not root.is_dir():
        print(f"❌  root is not a valid directory.")
        sys.exit(1)
print(f"🗂️  Scanning folder: root")
    pdf_index = build_index(root)
if not pdf_index:
        print("⚠️  No PDF files found. Exiting.")
        sys.exit(0)
print(f"\n✅  Index built – len(pdf_index) PDF(s) ready for searching.")
    interactive_search(pdf_index)
if __name__ == "__main__":
    main()

| Step | Activity | Owner | |------|----------|-------| | S1 | Draft outlines (theme + character). | Content Team | | S2 | Write full narratives (Telugu). | Professional writers (native speakers). | | S3 | Scientific fact‑check. | Medical Advisory Board. | | S4 | Cultural vetting (folk‑expert). | Cultural Advisory Panel. | | S5 | Rubric scoring (all criteria). | Quality Assurance Officer. | | S6 | Illustration design (local artists). | Graphic Design Team. | | S7 | Layout & PDF generation. | Production Team. | | S8 | Pilot test (10 families per theme). | Field Team → collect feedback. | | S9 | Final revision & sign‑off. | Project Lead. | “Once a weaver named Rangam wove a blanket