Skip to main content

YAML-driven ETL mappers for ForSITE soil database imports.

Project description

SOIL — Structured Observation Ingestion Library

Version: 0.1.0-a.2 (alpha) · Python: ≥ 3.10 · PyPI: soil-etl

YAML-driven ETL package for importing Canadian forestry soil datasets into the ForSITE PostGIS database model, built on the OGC Observations, Measurements & Samples specification.

Acknowledgements

Project Partners

This project was developed through joint funding provided by Agriculture and Agri-Food Canada (AAFC) under the CSBO program and Natural Resources Canada (NRCan) for the ForSITE-Soil degradation project.

The code developed as part of this project includes contributions from:


Installation

pip install soil-etl

Or with Poetry:

poetry add soil-etl

Quick Start

YAML-only pipeline (recommended)

from pathlib import Path
from soil_etl.yaml_mapper import YamlDatasetMapper

mapper = YamlDatasetMapper(
    config_path=Path("my_config.yaml"),
    data_file_path=Path("data.xlsx"),
)
metrics = mapper.import_data(
    database_connection_string="postgresql+psycopg2://user:password@host:5432/database",
)
print(metrics)

All new ETL pipelines must use YamlDatasetMapper. See AGENTS.md for full architecture guidance.

Canonical imports

from soil_etl.yaml_mapper import YamlDatasetMapper
from soil_etl.bindings import Binding, FromColumn
from soil_etl.db import ImportationInterface

Database Connection

Connection resolution priority (first non-null wins):

  1. Explicit database_connection_string argument to import_data()
  2. DB_CONN environment variable
  3. Hydra config directory (database_config_path / CONFIG_PATH env var)
# Option 1 — direct connection string
mapper.import_data(
    database_connection_string="postgresql+psycopg2://user:password@host:5432/database",
    database_engine_kwargs={"pool_size": 10, "pool_pre_ping": True},
)

# Option 2 — Hydra config files
mapper.import_data(
    database_config_path="configs",
    database_config_name="configs.yaml",
)

# Option 3 — environment variables
import os
os.environ["DB_CONN"] = "postgresql+psycopg2://user:password@host:5432/database"
mapper.import_data()

Environment variables recognized:

Variable Description
DB_CONN Full SQLAlchemy connection string
DB_USER Database username (used alongside Hydra config)
DB_PASSWORD Database password (used alongside Hydra config)
CONFIG_PATH Hydra config directory path
IMPORT_BATCH_SIZE Override batch size for this run (positive integer)
EXPORT_RETREATED_DATAFRAME Export transformed DataFrame to *_retreated.xlsx (1/true/yes)

import_data() Signature

mapper.import_data(
    error_file_path=Path("errors.xlsx"),            # defaults to <dataset_title>_ERROR.xlsx
    dry_run=False,                                   # True: extract + validate only, no DB writes
    database_connection_string="postgresql+...",
    database_config_path="configs",
    database_config_name="configs.yaml",
    database_engine_kwargs={"pool_size": 10},
) -> ImportRunMetrics

Returns an ImportRunMetrics dataclass:

@dataclass(frozen=True)
class ImportRunMetrics:
    rows_processed: int
    error_rows: int
    generic_results: int
    horizon_results: int
    feature_relationships: int
    sample_information: int
    batches: int
    batch_size: int
    dry_run: bool
    extract_seconds: float
    persist_seconds: float
    total_seconds: float

YAML Configuration

The YAML config drives the entire mapping: Excel reading parameters, project metadata, feature of interest hierarchy, observations, and results.

Reference files (packaged):

File Purpose
src/soil_etl/yaml_mapper/dummy_config_file.yaml Fully-documented boilerplate with all options
src/soil_etl/yaml_mapper/template.yaml Real-world example with YAML anchors
src/soil_etl/yaml_mapper/examples/on_master.yaml Province-specific example
examples/configs/example_config.yaml Additional annotated example

For full documentation of the YAML format, see docs/README_yaml_format_en.md.

Top-level sections

source:                        # Excel reading params (sheet_name, header, skiprows, null_values…)
export_retreated_dataframe: false
import_batch_size: 1000        # Rows per DB batch (default: 5000)
project:                       # Project metadata (strategy, name, abbrev, province, type…)
partners:                      # Partner organizations
stewards:                      # Data steward contacts
feature_relationships:         # Optional parent-child FOI hierarchies
sample_information:            # Optional sample metadata (type, depth, horizon, dimensions)
results:                       # Generic result bindings
horizon_results:               # Horizon-specific results (includes hz_from, hz_to)

Python Subclass (advanced)

When YAML alone cannot express the required logic, subclass YamlDatasetMapper and override properties:

from pathlib import Path
from soil_etl.yaml_mapper import YamlDatasetMapper
from soil_etl.model_bindings import FeatureOfInterestBinding

class MyMapper(YamlDatasetMapper):
    @property
    def _feature_of_interest(self) -> FeatureOfInterestBinding:
        # custom logic here
        ...

mapper = MyMapper(
    config_path=Path("my_config.yaml"),
    data_file_path=Path("data.xlsx"),
)
mapper.import_data(database_connection_string="postgresql+psycopg2://...")

See examples/subclass_runner.py for a complete example.


Development

pip install poetry
poetry install --with dev,test
poetry run pytest tests

Tests use testcontainers with postgis/postgis:17-3.4 — Docker must be running.

Linting & formatting

poetry run black src tests
poetry run ruff check src tests
poetry run isort src tests

Build & Publish

python -m build
twine check dist/*
twine upload dist/*

Links

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

soil_etl-0.1.0a5.tar.gz (102.6 kB view details)

Uploaded Source

Built Distribution

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

soil_etl-0.1.0a5-py3-none-any.whl (155.8 kB view details)

Uploaded Python 3

File details

Details for the file soil_etl-0.1.0a5.tar.gz.

File metadata

  • Download URL: soil_etl-0.1.0a5.tar.gz
  • Upload date:
  • Size: 102.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for soil_etl-0.1.0a5.tar.gz
Algorithm Hash digest
SHA256 0fe07a92892c533211c0f3501041419cdaec6957d5aad97d0d124913407af759
MD5 9239008712db5572cec1567a64ea04a1
BLAKE2b-256 7fa1cc6bc071bc8e0f925cc67e8a172768f1aaa2e9bf01c241aaf36c38b1d24d

See more details on using hashes here.

File details

Details for the file soil_etl-0.1.0a5-py3-none-any.whl.

File metadata

  • Download URL: soil_etl-0.1.0a5-py3-none-any.whl
  • Upload date:
  • Size: 155.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for soil_etl-0.1.0a5-py3-none-any.whl
Algorithm Hash digest
SHA256 3d47a507e29da4ba19dd35da0ea780e358e28054487326b81f78e23eab7f703b
MD5 8658c41bcc2df0d3fda3c4abd9ecfe72
BLAKE2b-256 d6e00571b7baedff94aa387e2515804cc26f04a0d90963bbf50cc9057ee633ea

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 Pingdom Monitoring Sentry Error logging StatusPage Status page