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.1.0.tar.gz (19.6 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.1.0-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: weji_gannet-0.1.0.tar.gz
  • Upload date:
  • Size: 19.6 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.1.0.tar.gz
Algorithm Hash digest
SHA256 2abf9f2bcb322252f127fecb8afcd8fcc083d205339bc6b8953a8871b6fccfe6
MD5 c10964301c7097355132b12d08b1f7bc
BLAKE2b-256 49e16cbfa14b43b7054b2bf2fd4df78fcaf796826c221f1c1392579a433ebddb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: weji_gannet-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.1 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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 65edc0b0968c246c9551830ebd8eabc995004de448a5c5fd30bf5fb10a7a8ad1
MD5 5115070fdf732c0bd3cebfc70324be1b
BLAKE2b-256 f38a07b5ce16f02adde7119f63ff3ae7ef455f504e297e82ae5a53b98156db0d

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