Skip to content

Architecture

Design principles

GdeltForge follows a single-responsibility, single-stage execution model: each command performs exactly one transformation.

Stage Does
scrape download raw GDELT CSV files
convert transform CSV -> Parquet
filter remove rows with missing values
sample reproducibly sample from Parquet files

This is intentionally transparent and low-magic:

  • every operation is explicit
  • no hidden steps
  • each module is individually testable
  • any stage can be re-run without affecting the others
┌─────────────┐      ┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│   Scraper   │ ---> │  Converter  │ ---> │   Filter    │ ---> │   Sampler   │
└─────────────┘      └─────────────┘      └─────────────┘      └─────────────┘
      CSV                Parquet            Cleaned data         Sampled data

Each stage consumes the previous stage's output, which gives you:

  • incremental execution (re-run only the stage you need)
  • streaming-friendly operations (batched/chunked reads, not full loads)
  • memory-efficient processing on datasets much larger than RAM
  • simple debugging (inspect intermediate Parquet at any point)
  • reusable intermediate data (multiple sampling runs off one filtered dataset)

Project structure

GdeltForge is a standard installable src/ package:

project_root/
├── config/
│ └── settings.yaml # Global configuration for all pipeline stages
│
├── src/gdeltforge/
│ ├── cli.py # Argument parsing + subcommand dispatch (the gdeltforge entry point)
│ │
│ ├── conversion/
│ │ └── converter.py # CSV -> Parquet conversion logic
│ │
│ ├── filtering/
│ │ └── filter.py # Filtering logic (drop invalid rows)
│ │
│ ├── sampling/
│ │ ├── indexer.py # File indexing for reproducible sampling
│ │ ├── rng.py # Random number generation helpers
│ │ └── samplers.py # Indexed, daily, and filtered sampling
│ │
│ ├── scraping/
│ │ └── scraper.py # Downloader for raw GDELT event files
│ │
│ └── utils/
│   ├── config.py # Config resolution (--config / env var / CWD) and YAML loading
│   ├── io.py # File and chunked-IO helpers
│   └── logging.py # Central logging system
│
├── tests/ # pytest suite (unit tests, no network/browser required)
│
├── main.py # Backward-compatible shim: `python main.py <command>` still works
├── pyproject.toml # Package metadata, build backend, gdeltforge console-script entry point
└── README.md

Each package under src/gdeltforge/ corresponds to one pipeline stage, plus utils/ for shared, stage-agnostic helpers (config loading, logging, I/O).

Logging

Logging goes through a shared helper used consistently across every module:

from gdeltforge.utils.logging import get_logger
logger = get_logger(__name__)

To also log to a file (used by the CLI entrypoint itself):

logger = get_logger(__name__, log_to_file=True)

File logs land in logs/pipeline.log, relative to the current working directory.