Skip to main content

weji-gannet

Extract, index, and search any collection of PDF documents.

License: MIT Python 3.11+

weji-gannet turns a collection of PDFs into a properly structured, queryable database — the same way a gannet dives into the ocean and surfaces with exactly what it went in for.

Point it at any PDF collection — scanned documents, government disclosures, research papers, contracts, archives — and get clean text extraction with full-text search and optional columnar analytics via weji-goose.

Built by WEJI Northern Technologies Inc. — 100% Indigenous-owned, Qalipu Mi'kmaq First Nation, Bay St. George, NL.


How it works

Your PDFs  →  per-page extraction cascade  →  SQLite FTS5   (full-text search)
                                           →  weji-goose     (analytics, optional)

Each page is processed independently through three passes in order:

  1. pdftotext — fast, lossless for digitally-generated PDFs
  2. tesseract 250 DPI / PSM 6 — OCR for scanned pages
  3. tesseract 300 DPI / PSM 3 — higher-res OCR for complex layouts

A hybrid PDF (typed cover page + scanned attachments) gets complete, correctly ordered text from both sections with no duplication.


Install

pip install weji-gannet

# System dependencies:
sudo apt install poppler-utils tesseract-ocr tesseract-ocr-eng   # Ubuntu/Debian
brew install poppler tesseract                                     # macOS

With weji-goose columnar analytics (optional):

pip install "weji-gannet[goose]"

Quick start — Python

from weji_gannet import Pipeline
from weji_gannet.sources import LocalDirectorySource

p = Pipeline("./output")
p.ingest(LocalDirectorySource("./my_pdfs"))

# Full-text search
results = p.query("search terms here")
for r in results:
    print(r["doc_id"], r["excerpt"])

# Keyword intelligence — define your own categories
report = p.intelligence(targets={
    "contracts":     ["procurement", "tender", "awarded", "vendor"],
    "environmental": ["contamination", "remediation", "PFAS"],
    "complaints":    ["violation", "enforcement", "cease and desist"],
})

print(p.stats())

Quick start — CLI

# Index a directory of PDFs
gannet --data-dir ./output ingest ./my_pdfs

# Search
gannet --data-dir ./output query "search terms"

# Intelligence report (define your targets in a JSON file)
echo '{"contracts": ["procurement", "tender"]}' > targets.json
gannet --data-dir ./output intel --targets targets.json

# Stats
gannet --data-dir ./output stats

Extending to any PDF source

Implement DocumentSource to pull PDFs from anywhere — a web portal, an S3 bucket, a database of URLs, an API:

from weji_gannet.sources import Document, DocumentSource
from pathlib import Path

class MySource(DocumentSource):
    def documents(self):
        for row in my_db.query("SELECT id, title, dept FROM docs"):
            yield Document(
                id=str(row.id),
                title=row.title,
                source=row.dept,
            )

    def download(self, doc, dest_dir):
        pdf_bytes = my_api.fetch(doc.id)
        dest = dest_dir / f"{doc.id}.pdf"
        dest.write_bytes(pdf_bytes)
        return dest

p = Pipeline("./output")
p.ingest(MySource())

Output structure

output/
├── documents.db          — SQLite FTS5 (full-text search index)
├── goose/
│   └── documents/        — weji-goose columnar store (analytics, if installed)
├── files/                — downloaded PDFs
├── report.txt            — intelligence query results (human-readable)
└── report.json           — intelligence query results (structured)

documents table schema

Column Description
doc_id unique document identifier
source originating collection or organisation
title short label / document summary
body extracted full text (all pages, joined by \f)
year / month document date
file_path local PDF path
source_url original URL
page_count total pages
ocr_method how text was extracted (pdftotext, tesseract, hybrid:dN/oM/bK, exhausted)

Part of the WEJI stack

Package Role
weji-goose Columnar storage engine — analytics layer
weji-gannet PDF extraction and indexing pipeline

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

weji_gannet-0.2.0.tar.gz (18.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

weji_gannet-0.2.0-py3-none-any.whl (20.9 kB view details)

Uploaded Python 3

File details

Details for the file weji_gannet-0.2.0.tar.gz.

File metadata

  • Download URL: weji_gannet-0.2.0.tar.gz
  • Upload date:
  • Size: 18.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for weji_gannet-0.2.0.tar.gz
Algorithm Hash digest
SHA256 da699b51c4dac3968dcacbb9a3c005cd283dc862c2ef41a99796b1756a84bd28
MD5 18dc38477cf639e89609c9c6909648f2
BLAKE2b-256 b238e0b58d9b90b23f06120318e2133ad0ef24c98d97923860a697cf4d01070c

See more details on using hashes here.

File details

Details for the file weji_gannet-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: weji_gannet-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 20.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for weji_gannet-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6cd5ac8ea73fa8270dc0dbe07acc3251cb13dd3ba0ac104bdcf6455a97eb5caa
MD5 951ae34ecd5a80f91bc50b548754abf4
BLAKE2b-256 61179f06e41e28f9f5579dacaf0eeb837b66ade8e0002eefe35333eacba5d58b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page