Skip to main content

Parse airline e-ticket PDFs into strongly-typed JSON with high accuracy.

Project description

eticket-document-sdk

Parse airline e-ticket PDFs and convert them into strongly-typed JSON with very high accuracy.

The SDK primarily targets PDFs that already contain a text layer (the common case for e-ticket receipts, booking confirmations and itineraries). OCR is used only as a fallback when text extraction is insufficient.

from eticket_document_sdk import ETicketParser

parser = ETicketParser()
booking = parser.parse("ticket.pdf")
print(booking.model_dump())
{
  "booking_code": "F65A7Y",
  "ticket_number": "7382421531079",
  "passenger": { "first_name": "YUMIKO", "last_name": "KOHNO" },
  "currency": "JPY",
  "total_price": 467510,
  "segments": [{ "flight_number": "VN311", "origin": "NRT", "destination": "HAN" }]
}

Table of Contents


Installation

pip install eticket-document-sdk

Base install includes PDF text extraction (PyMuPDF + pdfplumber). The OCR fallback backend is a heavy optional extra:

# Only needed for scanned / image-only PDFs
pip install "eticket-document-sdk[ocr]"
Extra Pulls in When you need it
(base) pydantic, pymupdf, pdfplumber, python-dateutil Text-layer PDFs (the common case)
ocr paddleocr, opencv-python, numpy Scanned/image PDFs requiring OCR
dev pytest, pytest-cov, ruff, build Contributing / running tests

Requires Python 3.10+. URL input requires no extra dependencies (uses stdlib urllib).


Quick Start

from eticket_document_sdk import ETicketParser

parser = ETicketParser()

# 1) From a local file path (PDF or image)
booking = parser.parse("ticket.pdf")

# 2) From a remote URL (http/https — GCS, S3, CDN, …)
booking = parser.parse("https://storage.googleapis.com/bucket/ticket.pdf")

# 3) From raw bytes
booking = parser.parse_bytes(pdf_bytes)

# 4) From already-extracted text (no PDF/OCR needed)
booking = parser.parse_text(raw_text)

print(booking.model_dump())          # python types
print(booking.model_dump(mode="json"))  # JSON-serializable (datetimes -> str)

# Convenience top-level accessors:
booking.booking_code     # "F65A7Y"
booking.ticket_number    # "7382421531079"
booking.currency         # "JPY"
booking.total_price      # 467510
booking.passenger.full_name
booking.segments[0].origin

Advanced Usage

Configuration

parser = ETicketParser(
    enable_ocr=True,             # allow OCR fallback (default True)
    ocr_langs=["vi", "en", "ja"],# OCR languages (Vietnamese/English/Japanese)
    debug=False,                 # enable DEBUG logging for the SDK
    strict_validation=False,     # raise ValidationError on invalid output
    text_quality_threshold=0.35, # below this, OCR fallback kicks in
)

URL input

parse() accepts any HTTP or HTTPS URL. The file is downloaded automatically using the Python standard library (no extra dependency required):

booking = parser.parse(
    "https://storage.googleapis.com/meetingsvn/cms/vnzamazing-tour/"
    "passport_images/F65A7Y.pdf"
)

Supported URL patterns:

Provider Example
Google Cloud Storage https://storage.googleapis.com/bucket/path/ticket.pdf
AWS S3 (public) https://s3.amazonaws.com/bucket/ticket.pdf
Azure Blob Storage https://account.blob.core.windows.net/container/ticket.pdf
Any CDN / direct link https://cdn.example.com/tickets/ABC123.pdf

Limit: 50 MB maximum. Private URLs (requiring auth headers/signed tokens) are not currently supported via this method — download the file first and use parse_bytes() instead.

Detailed result envelope

parse() returns a Booking. For metadata about how the document was parsed, use the *_detailed variants, which return a ParseResult:

result = parser.parse_detailed("ticket.pdf")
# or
result = parser.parse_detailed("https://storage.googleapis.com/bucket/ticket.pdf")

result.success            # True
result.parser             # "vietnam_airlines"
result.extraction_method  # ExtractionMethod.TEXT_LAYER | OCR | RAW_TEXT
result.confidence         # 0.0 .. 1.0 completeness heuristic
result.warnings           # list of non-fatal validation issues
result.booking            # the Booking object

Also available: parse_bytes_detailed(...) and parse_text_detailed(...).

Thread-safety & performance

A single ETicketParser instance is reusable and thread-safe. The PaddleOCR model, compiled regexes and the parser registry are created once and shared, so models are never reloaded between calls. Create one instance and reuse it (e.g. for batch processing — see examples/batch_parse.py).


Data Model

All models are Pydantic v2 (eticket_document_sdk.models, re-exported from eticket_document_sdk.schemas.pydantic_models).

Booking
├── status: TICKETED | CONFIRMED | PENDING | CANCELLED | UNKNOWN
├── passenger: Passenger
│   ├── title, first_name, last_name, full_name
│   ├── membership_number          # frequent-flyer number
│   └── passenger_type             # ADT | CHD | INF
├── segments: list[FlightSegment]
│   ├── segment_id, flight_number, airline
│   ├── origin, destination        # IATA codes
│   ├── departure_datetime, arrival_datetime
│   ├── departure_terminal, arrival_terminal
│   ├── cabin_class, fare_basis, seat
│   ├── free_baggage, status
├── ticket: Ticket
│   ├── booking_code, ticket_number, currency
│   ├── base_fare, taxes, total_price
│   ├── tax_breakdown: list[TaxItem]   # {code, amount}
│   ├── fare_basis, fare_calculation, endorsement
│   ├── issue_date, payment
└── booking_code / ticket_number / currency / total_price   # convenience mirrors

Parser Architecture

The SDK follows a clean, layered pipeline:

            ┌─────────────────────────────────────────────────────────┐
 source ──▶ │ 1. detect type (PDF / IMAGE / TEXT)        core.classifier│
            │ 2. extract text layer (PyMuPDF → pdfplumber) pdf.*         │
            │ 3. measure quality                          pdf.text_extr. │
            │ 4. OCR fallback if needed (PaddleOCR)        ocr.*          │
            │ 5. classify airline → pick parser           core.classifier│
            │ 6. parse fields                             parsers.*       │
            │ 7. validate (business rules)                core.validator  │
            │ 8. return Pydantic Booking                  models.*        │
            └─────────────────────────────────────────────────────────┘

Key design points:

  • Text-first. OCR is only attempted when the extracted text layer falls below the quality threshold. PyMuPDF is the primary extractor; pdfplumber is the fallback engine.
  • Lazy heavy deps. PyMuPDF, pdfplumber, PaddleOCR, OpenCV and numpy are all imported lazily, so importing the SDK is cheap and the OCR stack is optional.
  • Centralized regex (utils/regex.py) compiled once at import time.
  • Pluggable parsers via a thread-safe registry (see below).

Custom Parser Plugin

Add a new airline without modifying the SDK core. Implement BaseParser and register it:

from eticket_document_sdk import (
    BaseParser, Booking, Ticket, FlightSegment, Passenger, register_parser,
)
from eticket_document_sdk.parsers.generic.parser import GenericExtractors


class JapanAirlinesParser(BaseParser):
    code = "JL"
    name = "japan_airlines"

    def can_parse(self, text: str) -> float:
        # Return 0..1 confidence that this layout is yours.
        return 0.9 if "JAPAN AIRLINES" in text.upper() else 0.0

    def parse(self, text: str) -> Booking:
        return Booking(
            ticket=Ticket(
                booking_code=GenericExtractors.find_pnr(text),
                ticket_number=GenericExtractors.find_ticket_number(text),
                currency=GenericExtractors.find_currency(text),
            ),
            passenger=Passenger(),
            segments=[],
        )


register_parser("JL", JapanAirlinesParser())

# From now on ETicketParser auto-selects it for matching tickets.

The classifier calls can_parse() on every registered parser and selects the highest scorer; airline-specific parsers always win ties against the generic fallback. See examples/custom_parser.py for a runnable version.

This is the same mechanism by which future airlines (ANA, Air France, Emirates, Singapore Airlines, …) are added.


Error Handling

All exceptions derive from ETicketSDKError:

Exception Raised when
DocumentReadError File can't be opened/read, or is empty/corrupt
UnsupportedDocumentError Input type/layout not supported
OCRFailedError OCR needed but disabled/unavailable, or produced nothing
ParserError No parser matched, or a parser failed
ValidationError Output failed business-rule validation (strict mode)
from eticket_document_sdk import ETicketParser, ETicketSDKError

try:
    booking = ETicketParser().parse("ticket.pdf")
except ETicketSDKError as exc:
    print(f"Failed: {exc}")

Validation (core/validator.py) checks ticket-number format (13 digits), PNR format (6 alphanumerics), currency code, flight-number format and segment presence. In non-strict mode, issues are returned as result.warnings; in strict_validation=True mode a ValidationError is raised.


Development Guide

git clone https://github.com/your-org/eticket-document-sdk
cd eticket-document-sdk

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# Run the test suite with coverage (target: 90%+)
pytest --cov=eticket_document_sdk --cov-report=term-missing

# Lint / format
ruff check .
ruff format .

Tests use a bundled real Vietnam Airlines fixture (tests/fixtures/). OCR-path tests use a fake engine, so PaddleOCR is not required to run the suite.

Project layout mirrors the pipeline: core/ (orchestration), pdf/ (extraction), ocr/ (fallback), parsers/ (plugins), models/ + schemas/ (Pydantic), utils/, exceptions/.


Release Guide

  1. Update the version in pyproject.toml and eticket_document_sdk/__init__.py (__version__).
  2. Update the changelog / release notes.
  3. Run the full test suite and linters; ensure coverage ≥ 90%.
  4. Build the distributions:
    python -m build
    
    This produces dist/eticket_document_sdk-<version>-py3-none-any.whl and the sdist .tar.gz.
  5. Smoke-test the wheel in a clean virtualenv:
    pip install dist/eticket_document_sdk-*.whl
    
  6. Publish:
    python -m twine upload dist/*
    
  7. Tag the release: git tag v<version> && git push --tags.

Roadmap

The extractor/parser boundary is deliberately abstract so additional backends can be added without breaking the public API:

  • Cloud OCR / Document AI: Azure Document Intelligence, AWS Textract, Google Document AI — pluggable as alternative OCREngine implementations or text providers.
  • LLM structured outputs: OpenAI and Claude structured outputs as alternative parser plugins behind the same BaseParser contract.

Because selection happens via the registry + classifier, these can be layered in as new strategies while ETicketParser.parse(...) stays unchanged.


License

MIT — see LICENSE.

Project details


Download files

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

Source Distribution

eticket_document_sdk-1.3.0.tar.gz (279.0 kB view details)

Uploaded Source

Built Distribution

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

eticket_document_sdk-1.3.0-py3-none-any.whl (48.8 kB view details)

Uploaded Python 3

File details

Details for the file eticket_document_sdk-1.3.0.tar.gz.

File metadata

  • Download URL: eticket_document_sdk-1.3.0.tar.gz
  • Upload date:
  • Size: 279.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for eticket_document_sdk-1.3.0.tar.gz
Algorithm Hash digest
SHA256 643451a38a1050f013afdd9df7a4114cd0bdad0d18aec576c4279efd56eb7df8
MD5 5e2c6497e823991d6c37e4690b36cf9c
BLAKE2b-256 242ff87337f18805ac6f120adc2af6b48756122157fb36f23d14b69a2bff1edf

See more details on using hashes here.

Provenance

The following attestation bundles were made for eticket_document_sdk-1.3.0.tar.gz:

Publisher: publish.yml on hailong2107/eticket-document-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file eticket_document_sdk-1.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for eticket_document_sdk-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b81c404f753426ecf9ea4708a9c72b65ca237e79aacb61289299ccd80d9eb67c
MD5 ac443c947e63e491dd5ea1328dfa4f41
BLAKE2b-256 ace95b8238c5367732b41783350a86704df0258963d65339257dd28083b52117

See more details on using hashes here.

Provenance

The following attestation bundles were made for eticket_document_sdk-1.3.0-py3-none-any.whl:

Publisher: publish.yml on hailong2107/eticket-document-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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