Metadata-Version: 2.4
Name: biotailor
Version: 0.1.14
Summary: Python SDK for the Biotailor bioinformatics pipeline platform
Author: Biotailor Team
License-Expression: MIT
Keywords: bioinformatics,biotailor,nextflow,pipeline
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.9
Requires-Dist: requests>=2.28.0
Requires-Dist: tqdm>=4.64.0
Provides-Extra: dev
Requires-Dist: pytest-mock>=3.10; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: responses>=0.23; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# biotailor

Python SDK for the [Biotailor](https://biotailor.org) bioinformatics pipeline platform.

## Table of Contents

- [Installation](#installation)
- [Quickstart](#quickstart)
- [API Reference](#api-reference)
  - [Client: `BiotailorClient`](#client-biotailorclient)
    - [Tool discovery](#tool-discovery)
    - [Dataset discovery](#dataset-discovery)
    - [Job management](#job-management)
    - [Running and monitoring pipelines](#running-and-monitoring-pipelines)
  - [Tools: `ToolConfig`](#tools-toolconfig)
  - [Pipelines: `Pipeline`](#pipelines-pipeline)
  - [Models](#models)
  - [Exceptions](#exceptions)
- [Development](#development)
- [Publishing to PyPI](#publishing-to-pypi)

<a id="installation" name="installation"></a>

## Installation

```bash
pip install biotailor
```

<a id="quickstart" name="quickstart"></a>

## Quickstart

```python
from biotailor import BiotailorClient, Pipeline

# 1. Create a client (get your API key from the Biotailor website)
client = BiotailorClient(api_key="btk_xxx.sk_yyy")

# 2. Discover available tools
client.print_tools()

# 3. Build a pipeline
tool = client.get_tool("fastp-single")

pipeline = (
    Pipeline(name="QC my reads", tool=tool)
    .set_input("input", "reads.fastq")
    .set_param("qualified_quality_phred", 20)
)

# 4. Run and wait
job = client.run_and_wait(pipeline, output_dir="./results")
print(f"Job {job.jobid} finished with status: {job.job_status.value}")
```

<a id="api-reference" name="api-reference"></a>

## API Reference

Import the main SDK objects from `biotailor`:

```python
from biotailor import BiotailorClient, Pipeline
```

<a id="client-biotailorclient" name="client-biotailorclient"></a>

### Client: `BiotailorClient`

Create one client per API key. By default, it uses the production API at `https://api.biotailor.org`.

```python
client = BiotailorClient(
    api_key="btk_xxx.sk_yyy",
    base_url="https://api.biotailor.org",
    timeout=30,
    debug=False,
)
```

<a id="tool-discovery" name="tool-discovery"></a>

#### Tool discovery

| Method              | Returns            | Description                                                    |
| ------------------- | ------------------ | -------------------------------------------------------------- |
| `list_tools()`      | `list[ToolConfig]` | Fetch all available tool configurations.                       |
| `get_tools()`       | `list[ToolConfig]` | Alias for `list_tools()`.                                      |
| `print_tools()`     | `None`             | Print all tools as a human-readable table.                     |
| `get_tool(tool_id)` | `ToolConfig`       | Fetch one tool by ID, such as `"fastp-single"` or `"bowtie2"`. |

<a id="dataset-discovery" name="dataset-discovery"></a>

#### Dataset discovery

| Method                      | Returns         | Description                                                    |
| --------------------------- | --------------- | -------------------------------------------------------------- |
| `list_datasets(type=None)`  | `list[Dataset]` | Fetch available datasets, optionally filtered by dataset type. |
| `print_datasets(type=None)` | `None`          | Print datasets grouped by dataset type.                        |

Example:

```python
client.print_datasets(type="bowtie2_db")
datasets = client.list_datasets()
```

<a id="job-management" name="job-management"></a>

#### Job management

| Method                                                                         | Returns     | Description                                           |
| ------------------------------------------------------------------------------ | ----------- | ----------------------------------------------------- |
| `list_jobs(sortBy="latestStatusTimestamp", order="desc", jobNameFilter=None)`  | `list[Job]` | Fetch jobs, with optional sorting and name filtering. |
| `print_jobs(sortBy="latestStatusTimestamp", order="desc", jobNameFilter=None)` | `None`      | Print jobs as a human-readable table.                 |
| `get_job(job_id)`                                                              | `Job`       | Fetch current status and metadata for one job.        |
| `cancel_job(job_id)`                                                           | `None`      | Cancel a running job.                                 |

`sortBy` supports `"latestStatusTimestamp"` and `"startTimestamp"`. `order` supports `"asc"` and `"desc"`.

<a id="running-and-monitoring-pipelines" name="running-and-monitoring-pipelines"></a>

#### Running and monitoring pipelines

| Method                                                                                        | Returns      | Description                                                                                                 |
| --------------------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------- |
| `run(pipeline, show_progress=True)`                                                           | `Job`        | Create a job, submit the workflow, upload input files, and return the initial job.                          |
| `wait_for_completion(job_id, poll_interval=10, timeout=None, verbose=True)`                   | `Job`        | Poll until the job reaches a terminal status. Prints status updates and running logs when `verbose=True`.   |
| `run_and_wait(pipeline, output_dir=None, poll_interval=10, timeout=None, show_progress=True)` | `Job`        | Convenience method that runs a pipeline, waits for completion, and optionally downloads outputs on success. |
| `get_job_logs(job_id, process_id)`                                                            | `str`        | Fetch formatted logs for a specific process in a job.                                                       |
| `get_output_file_sizes(job_id)`                                                               | `list[dict]` | Fetch output file size metadata for a completed job.                                                        |
| `download_outputs(job_id, output_dir, process_id=None, show_progress=True)`                   | `list[str]`  | Download output files for all processes, or one process if `process_id` is provided.                        |

<a id="tools-toolconfig" name="tools-toolconfig"></a>

### Tools: `ToolConfig`

`ToolConfig` describes one Biotailor tool. You usually get it from `client.get_tool()` or `client.list_tools()`.

Important fields:

| Field              | Description                                                     |
| ------------------ | --------------------------------------------------------------- |
| `toolid`           | Tool identifier used by the API.                                |
| `display_name`     | Human-readable tool name.                                       |
| `category`         | Tool category, such as quality control or alignment.            |
| `description`      | Optional long-form tool description.                            |
| `has_sepe`         | Whether the tool supports both single-end and paired-end modes. |
| `default_hardware` | Default `DefaultHardware` with CPU and RAM settings.            |
| `parameters`       | List of `ToolParameter` objects.                                |
| `outputs`          | List of `Output` objects.                                       |

Methods:

| Method               | Returns | Description                                                                            |
| -------------------- | ------- | -------------------------------------------------------------------------------------- |
| `tool_guide()`       | `str`   | Return a readable guide with tool info, parameters, outputs, and sample pipeline code. |
| `print_tool_guide()` | `None`  | Print the same guide returned by `tool_guide()`.                                       |

Example:

```python
tool = client.get_tool("bowtie2")
tool.print_tool_guide()
```

<a id="pipelines-pipeline" name="pipelines-pipeline"></a>

### Pipelines: `Pipeline`

`Pipeline` is a chainable builder for one tool run. Pass either a `ToolConfig` object or a tool ID string.

```python
pipeline = Pipeline(name="my-job", tool=tool)
```

Builder methods:

| Method                                            | Returns    | Description                                                                                                                |
| ------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------- |
| `set_param(param_id, value)`                      | `Pipeline` | Set one non-file tool parameter.                                                                                           |
| `set_params(**kwargs)`                            | `Pipeline` | Set multiple non-file parameters at once.                                                                                  |
| `set_input(param_id, file_path)`                  | `Pipeline` | Set one local file input.                                                                                                  |
| `set_input_batch(param_id, file_paths)`           | `Pipeline` | Set multiple local files for a batch input.                                                                                |
| `set_dataset(param_id, dataset_type, dataset_id)` | `Pipeline` | Use a platform dataset as an input source.                                                                                 |
| `set_hardware(cpu, ram_gb)`                       | `Pipeline` | Override default hardware. CPU must be one of the supported powers of two; RAM must be `cpu * 2`, `cpu * 4`, or `cpu * 8`. |
| `set_pair_end()`                                  | `Pipeline` | Mark the run as paired-end. Required for tools that support both single-end and paired-end modes.                          |
| `set_single_end()`                                | `Pipeline` | Mark the run as single-end. Required for tools that support both single-end and paired-end modes.                          |

Utility methods:

| Method                                    | Returns          | Description                                                                                                                                |
| ----------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `validate(available_datasets=None)`       | `None`           | Validate file paths, hardware, required parameters, parameter value types, SE/PE mode compatibility, and optional dataset catalog matches. |
| `build_workflow(available_datasets=None)` | `dict`           | Build the workflow JSON sent to the API. Calls `validate()` first.                                                                         |
| `get_file_map()`                          | `dict[str, str]` | Return the local file map used by the uploader.                                                                                            |
| `has_dataset_inputs()`                    | `bool`           | Return whether the pipeline uses any platform datasets.                                                                                    |

For tools with `has_sepe=True`, always call either `set_single_end()` or `set_pair_end()` before running.

<a id="models" name="models"></a>

### Models

The SDK uses dataclasses for API responses and tool definitions.

| Model             | Purpose                                   | Key fields                                                                                                                                             |
| ----------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Job`             | Job metadata and status.                  | `jobid`, `job_name`, `job_status`, `start_timestamp`, `latest_status_timestamp`, `end_timestamp`, `estimate_cost`, `processes_name`, `workflow`, `raw` |
| `JobStatus`       | Job status enum.                          | `BUILDING`, `UPLOADING`, `STARTING`, `RUNNING`, `FAILED`, `SUCCEEDED`, `CANCELLED`; use `JobStatus.terminal_statuses()` for terminal states.           |
| `Dataset`         | Platform dataset metadata.                | `dataset_id`, `display_name`, `description`, `dataset_type`, `compression_method`, `s3uri`, `raw`                                                      |
| `ToolConfig`      | Full tool configuration.                  | `toolid`, `display_name`, `category`, `has_sepe`, `default_hardware`, `parameters`, `outputs`, `description`                                           |
| `ToolParameter`   | One configurable tool input or parameter. | `id`, `display_name`, `type`, `description`, `default_value`, `validation`, `is_pair_end_only`, `is_single_end_only`, `alias`                          |
| `Validation`      | Validation rules for a parameter.         | `required`, `minimum`, `maximum`, `options`, `accepted_input_types`, `accepted_compression_method`                                                     |
| `Option`          | One allowed option for a parameter.       | `display_name`, `value`, `description`                                                                                                                 |
| `Output`          | One declared tool output.                 | `id`, `display_name`, `description`, `file_type`, `file_name`, `compression_method`, `is_pair_end_only`, `is_single_end_only`, `alias`                 |
| `DefaultHardware` | Default tool hardware.                    | `cpu`, `ram_in_gb`                                                                                                                                     |

<a id="exceptions" name="exceptions"></a>

### Exceptions

All SDK exceptions inherit from `BiotailorError`.

| Exception                  | When it is raised                                                                           |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `BiotailorAPIError`        | The API returns a non-2xx response. Includes `status_code`, `message`, and `response_body`. |
| `BiotailorValidationError` | Client-side validation fails before a request or upload.                                    |
| `BiotailorUploadError`     | Uploading an input file fails.                                                              |

<a id="development" name="development"></a>

## Development

```bash
# Install in dev mode
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Lint
ruff check src/ tests/
```

<a id="publishing-to-pypi" name="publishing-to-pypi"></a>

## Publishing to PyPI

```bash
# 1. Install build tools
pip install build twine

# 2. Clean old builds and build the package
rm -rf dist/
python -m build

# 4. Upload to PyPI
twine upload dist/*
```

> **Note:** Bump `version` in `pyproject.toml` before each release — PyPI rejects duplicate versions. Use a [PyPI API token](https://pypi.org/manage/account/token/) for authentication (username: `__token__`).
