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

  1. Index all PDF files it finds.
  2. Extract the title/author (if present) from each PDF’s metadata.
  3. Search the index for keywords you care about – e.g. “Telugu”, “family”, “Dengudu Kathalu”, “56”, “better”, etc.
  4. Open the matching PDFs with the default system viewer (or print their paths so you can handle them however you like).

You can think of it as a lightweight “PDF‑catalogue‑and‑search” feature that works completely offline – no need to scrape the web or worry about copyright‑protected content.


2. The full script (copy‑paste it)

#!/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()

8. Work Plan & Resource Allocation

| Role | FTE (full‑time equivalents) | Cost (₹ 000) | Key Deliverables | |------|-----------------------------|--------------|------------------| | Project Manager | 0.6 | 720 | Overall coordination, timeline adherence | | Content Lead (Writer) | 0.8 | 960 | 56 stories, Teacher’s Guide | | Medical Advisor | 0.3 | 360 | Fact‑checking, health‑message alignment | | Cultural Advisor (Folklorist) | 0.3 | 300 | Cultural vetting, dialect checks | | Graphic Designer | 0.5 | 600 | Illustrations, PDF layout | | Field Coordinators (2) | 1.0 each | 1 200 each | Community workshops, pilot testing | | M&E Officer | 0.5 | 450 | Surveys, data analysis | | Administrative Support | 0.4 | 240 | Logistics, procurement | | Total | 5.2 | 5 730 | — | work+telugu+family+dengudu+kathalu+pdf+56+better

Note: Budget excludes printing (to be funded by partner NGOs) and contingency (10 %). Index all PDF files it finds


4. Download the PDF – What You’ll Get

| Feature | Description | |---------|-------------| | 56 Fully Illustrated Stories | Each tale comes with a simple black‑and‑white illustration that helps visual learners. | | PDF Optimized for Mobile & Print | 1 MB file size, searchable text, and print‑ready 8.5 × 11 in layout. | | “Quick‑Take” Sidebar | One‑sentence moral highlighted in bold for instant recall. | | Reflection Prompts | 56 prompts (one per story) for journaling or group discussion. | | Bonus: “Work‑Life Balance Checklist” | A one‑page printable checklist derived from the stories’ themes. | You can think of it as a lightweight

⬇️ Click Here to Download the Free 56‑Story PDF (PDF, 1 MB)
(The link is a placeholder – replace with your actual download URL.)