Skip to main content

Turba Client is a Python client for querying publicly accessible soil profile and fertilizer recommendation workflows across Morocco.

Project description

turba-client

A Python client for querying publicly accessible soil profile and fertilizer recommendation workflows across Morocco.

Query site-level fertilizer recommendations from geographic coordinates, inspect available crops and yield-target ranges, and scale from one field to many with a clean Python API and CLI.

PyPI Socket Downloads License: MIT


Why turba_client?

turba_client is a production-friendly Python library that makes it easy to query site-specific NPK fertilizer recommendations from fertimap.ma using only a field location, with optional agronomic overrides.

It is designed for researchers, agronomists, and developers who want a reproducible way to move from:

  • coordinates
  • to site context
  • to available crops and yield targets
  • to fertilizer recommendations
  • and finally to batch processing and downstream analysis

The library supports both single-field workflows and multi-field pipelines, with a clean Python interface and command-line support.


What you can do with it

With turba_client, you can:

  • query a site from its longitude and latitude
  • inspect soil and administrative context returned for that site
  • list the crops available at that location
  • request recommendations for:
    • one crop or many crops
    • one target-yield level or many target-yield levels
    • explicit numeric target yields
  • override selected field values when needed:
    • crop_name
    • target_yield_level
    • ph
    • matiere_organique_pct
    • p_assimilable_mgkg_p2o5
    • k_mgkg_k2o
  • process many fields at once from a DataFrame or CSV
  • map user column names to the library schema before batch processing

Installation

From PyPI

pip install turba-client

Development install

pip install -e .[dev]

Quick start

from turba_client import TurbaClient

client = TurbaClient()

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield_level="medium",
)

print(df[["crop_name", "target_yield_level", "N_kg_ha", "P_kg_ha", "K_kg_ha"]])

Expected output

        crop_name target_yield_level  N_kg_ha  P_kg_ha  K_kg_ha
0      Wheat (Rainfed)    medium    ...      ...      ...

User Workflow

The typical turba_client workflow is intentionally simple:

1) Start with a site

You provide coordinates.

from turba_client import TurbaClient

client = TurbaClient()

site = client.get_site_profile(
    longitude=-7.616,
    latitude=33.589,
)

print(site)

Expected output

SiteProfile(
    longitude=-7.616,
    latitude=33.589,
    region='...',
    province='...',
    commune='...',
    soil_type='...',
    texture_globale='...',
    ph=...,
    matiere_organique_pct=...,
    p_assimilable_mgkg_p2o5=...,
    k_mgkg_k2o=...
)

This step answers: What does turba-client know about this field?


2) Discover the crops available for that site

crops = client.list_crops(
    longitude=-7.616,
    latitude=33.589,
)

print(crops[["crop_id", "crop_name", "target_yield_min", "target_yield_max", "target_yield_step", "target_yield_unit"]])

Expected output

   crop_id      crop_name        target_yield_min  target_yield_max  target_yield_step  target_yield_unit
0          1      Wheat (Rainfed)          ...      ...      ...      ...
1          2      Wheat (Irrigated)        ...      ...      ...      ...
2          3      Barley (Rainfed)         ...      ...      ...      ...
...

This step answers: Which crops are valid here, and what are the allowed target-yield ranges?


3) Choose a target strategy

You can request recommendations using:

  • a standard target level: "low", "medium", "high"
  • several target levels at once
  • one or more explicit numeric target yields

A. One target level

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield_level="medium",
)

B. Multiple target levels

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield_level=["low", "medium", "high"],
)

C. A custom numeric target yield

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield=45,
)

D. Multiple custom numeric target yields

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield=[35, 45, 55],
)

If a numeric target_yield falls outside the valid crop range returned by turba-client for that site, turba_client raises a validation error.

This step answers: What production target am I optimizing for?


4) Request recommendations

Single crop

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield_level="high",
)

print(df[[
    "crop_name",
    "target_yield_level",
    "target_yield_value",
    "N_kg_ha",
    "P_kg_ha",
    "K_kg_ha",
]])

Expected output

      crop_name target_yield_level  target_yield_value  N_kg_ha  P_kg_ha  K_kg_ha
0    Wheat (Rainfed)      high      ...      ...      ...      ...

Multiple crops at once

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name=["Wheat (Rainfed)", "Barley (Rainfed)"],
    target_yield_level="medium",
)

All available crops for a site

If crop_name is omitted, the client can return recommendations across the valid crop space for the requested target strategy.

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    target_yield_level="medium",
)

This step answers: What N, P, and K recommendations should I use under the chosen crop and target settings?


5) Override selected field values when needed

For scenario testing, controlled comparisons, or curated field data, you can override the values used in the recommendation request.

df = client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield_level="medium",
    ph=7.8,
    matiere_organique_pct=1.9,
    p_assimilable_mgkg_p2o5=28,
    k_mgkg_k2o=210,
)

This is useful when:

  • you want to compare default vs curated values
  • you have field-lab measurements that should replace parsed defaults
  • you want reproducible sensitivity analyses

6) Scale to many sites

turba_client supports batch processing from pandas DataFrames and CSV files.

Batch from a DataFrame

import pandas as pd
from turba_client import TurbaClient

client = TurbaClient()

sites = pd.DataFrame([
    {
        "longitude": -7.616,
        "latitude": 33.589,
        "crop_name": "Wheat (Rainfed)",
        "target_yield_level": "medium",
    },
    {
        "longitude": -6.832,
        "latitude": 33.972,
        "crop_name": "Barley (Rainfed)",
        "target_yield_level": ["low", "high"],
        "matiere_organique_pct": 1.5,
        "p_assimilable_mgkg_p2o5": 20,
        "k_mgkg_k2o": 160,
    },
])

results = client.get_recommendations_batch(sites)
print(results.head())

Batch with custom column names

If your input data uses different column names, map them first.

prepared = client.prepare_input_table(
    sites,
    column_map={
        "longitude": "lon",
        "latitude": "lat",
        "crop_name": "crop",
        "target_yield_level": "target_level",
    },
)

results = client.get_recommendations_batch(prepared)

This step answers: How do I move from one field to a full operational dataset?


Python API

Main public interface

from turba_client import TurbaClient

TurbaClient(...)

Create a client.

client = TurbaClient(
    timeout=20,
    sleep_seconds=0.2,
    max_retries=3,
    user_agent="turba-client/0.1.0",
)

get_site_profile(...)

Inspect the site-level administrative and soil context.

client.get_site_profile(longitude=-7.616, latitude=33.589)

list_crops(...)

List available crops and target-yield ranges for a site.

client.list_crops(longitude=-7.616, latitude=33.589)

get_recommendations(...)

Get recommendations for one site.

client.get_recommendations(
    longitude=-7.616,
    latitude=33.589,
    crop_name="Wheat (Rainfed)",
    target_yield_level="medium",
)

Supports:

  • one or more crops
  • one or more target_yield_level values
  • one or more target_yield values
  • optional overrides

prepare_input_table(...)

Normalize input tables before batch runs.

client.prepare_input_table(df, column_map={...})

get_recommendations_batch(...)

Process multiple rows in a batch.

client.get_recommendations_batch(df)

CLI usage

The package installs a command-line tool named turba-client.

Single-site request

turba-client recommend-site \
  --longitude -7.616 \
  --latitude 33.589 \
  --crop-name "Wheat (Rainfed)" \
  --target-yield-level medium

Multiple target levels

turba-client recommend-site \
  --longitude -7.616 \
  --latitude 33.589 \
  --crop-name "Wheat (Rainfed)" \
  --target-yield-level low \
  --target-yield-level medium \
  --target-yield-level high

Custom numeric target yield

turba-client recommend-site \
  --longitude -7.616 \
  --latitude 33.589 \
  --crop-name "Wheat (Rainfed)" \
  --target-yield 45

Batch from CSV

turba-client recommend-many \
  --input-file sites.csv \
  --output results.csv

Validation and errors

The library validates:

  • coordinate ranges
  • accepted target level names
  • target-yield bounds against crop-specific allowed ranges
  • required columns for batch processing

Typical exceptions include:

  • ValidationError
  • CropNotFoundError
  • SiteDataNotFoundError
  • UpstreamResponseError

Example:

from turba_client import (
    TurbaClient,
    ValidationError,
    CropNotFoundError,
    SiteDataNotFoundError,
    UpstreamResponseError,
)

client = TurbaClient()

try:
    df = client.get_recommendations(
        longitude=-7.616,
        latitude=33.589,
        crop_name="Non Existing Crop",
    )
except ValidationError as e:
    print("Invalid input:", e)
except CropNotFoundError as e:
    print("Crop not found:", e)
except SiteDataNotFoundError as e:
    print("No site data found:", e)
except UpstreamResponseError as e:
    print("Unexpected upstream response:", e)

Example files

The examples/ directory contains end-to-end scripts:

  • basic_usage.py
  • custom_targets.py
  • batch_with_column_mapping.py
  • user_workflow.py

These examples are intended to mirror real user journeys:

  • inspect a site
  • list crops
  • request recommendations
  • run batch processing
  • export results

Development

Run tests

pytest

Build distributions

python -m build

This creates wheel and source-distribution artifacts in dist/.


Acknowledgment

This library builds on the valuable work of the fertimap.ma, which developed and maintains the underlying platform and recommendation service. turba_client is an independent Python client created to make fertimap.ma workflows more accessible for developers, researchers, and applied agronomy use cases. It does not claim ownership of the platform, its data, or its recommendation engine.

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

turba_client-0.1.0.tar.gz (22.9 kB view details)

Uploaded Source

Built Distribution

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

turba_client-0.1.0-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: turba_client-0.1.0.tar.gz
  • Upload date:
  • Size: 22.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for turba_client-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0b3cb8fcc0a2eda9f7e9192d34a8b082fdce2cb06ceb704d0ff384239046bf95
MD5 2e5eb8797ed7dccb9835d0289800b56d
BLAKE2b-256 7080b43ebf3567b01e1073d9474b53ec14ee7cb4e7ca1e28a67763372d235e03

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for turba_client-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c3aaed2d85e2676c85c9be33229392686bf9eedb3a9015d49a5a849998c2f7f5
MD5 73a2c339a0f1e1c8fa7faf203114b85f
BLAKE2b-256 89011fbe980bc072986e2d91fc2d2800b1a7d083b8f3693eecb92f309ff6cfd0

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