Metadata-Version: 2.4
Name: wittgen-b2sc
Version: 0.2.0
Summary: Thin Python client for the WittGen B2SC API (bulk RNA-seq -> single-cell type proportions)
Author: WittGen
License: Apache-2.0
Project-URL: Homepage, https://www.wittgenbio.com
Project-URL: Documentation, https://www.wittgenbio.com/api/v1/docs
Keywords: bioinformatics,rna-seq,single-cell,deconvolution,cell-type,wittgen,b2sc
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25
Provides-Extra: pandas
Requires-Dist: pandas>=1.3; extra == "pandas"
Provides-Extra: dev
Requires-Dist: pandas>=1.3; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# wittgen-b2sc

Thin Python client for the **WittGen B2SC API** — submit a bulk RNA-seq job, poll it to
completion, and pull per-sample single-cell type proportions, gene scores and plots. Built to
the OpenAPI contract at `https://www.wittgenbio.com/api/v1/openapi.json`.

## Install

```bash
pip install wittgen-b2sc            # core
pip install "wittgen-b2sc[pandas]"  # + DataFrame support
```

Installing from git requires access to the private platform repo, so it only works for WittGen
staff and partners who have been granted it:

```bash
pip install "wittgen-b2sc[pandas] @ git+ssh://git@github.com/WittGen-Inc/wittgen-b2sc-platform.git#subdirectory=clients/wittgen-b2sc-python"
```

## Authenticate

Create a per-user API key in the WittGen dashboard (it is shown once — store it as a secret).

```python
from wittgen_b2sc import B2SCClient
client = B2SCClient(api_key="wgk_...")
```

## Quickstart

```python
# 1. Submit a job against a built-in reference dataset
job = client.submit_job("SLE_status_3000", source="reference_dataset")
job_id = job["job_id"]

# 2. Wait for the GPU pipeline, then for the R analysis stage.
#    ~26 min for this SLE cohort; the 405-sample BRCA cohort takes over two hours.
client.wait_for_completion(job_id, wait_for_analysis=True)

# 3. Per-sample cell-type proportions as a tidy DataFrame
df = client.get_proportions(job_id, as_dataframe=True)   # columns: sample, cell_type, proportion
print(df.head())

# 4. Gene scores and plots from the R stage, as presigned downloads
for f in client.list_files(job_id, category="r_outputs"):
    print(f["name"], f["url"])
```

## Databricks quickstart

Run inside a Databricks notebook. Store the key in a **secret scope**, never inline.

```python
# Cell 1 — install
%pip install "wittgen-b2sc[pandas]"

# Cell 2 — client (key from a Databricks secret scope)
from wittgen_b2sc import B2SCClient
api_key = dbutils.secrets.get(scope="wittgen", key="b2sc_api_key")
client = B2SCClient(api_key=api_key)

# Cell 3 — submit + await + load as a Spark-ready pandas DataFrame
job = client.submit_job("SLE_status_3000", source="reference_dataset")
client.wait_for_completion(job["job_id"])
pdf = client.get_proportions(job["job_id"], as_dataframe=True)
sdf = spark.createDataFrame(pdf)      # -> a Spark DataFrame you can join/aggregate/save to Delta
sdf.display()
```

## API surface

| Method | Description |
|---|---|
| `list_models()` | Available disease models |
| `get_usage()` | Your plan and remaining trial quota |
| `upload_file(path, content_type="text/plain")` | Upload your own matrix → `input_file_key` |
| `submit_job(disease_model, source, input_file_key=None)` | Submit a job → job record |
| `get_job(job_id)` | Current status |
| `wait_for_completion(job_id, poll_interval=15, timeout=10800, wait_for_analysis=False)` | Poll until COMPLETED (or, with `wait_for_analysis=True`, until the R stage reaches R_COMPLETE) |
| `get_results_data(job_id)` | Aggregated summary (mean proportions across the cohort) + metadata |
| `get_proportions(job_id, fmt="long"\|"wide", as_dataframe=False)` | Per-sample proportions |
| `list_files(job_id, category=None)` | Every artifact with a presigned URL — gene scores, plots, matrices |

### Analysing your own data

`upload_file()` wraps the two-step presigned-S3 handshake — the PUT is signed over its headers,
so hand-rolling it is easy to get wrong:

```python
key = client.upload_file("my_cohort.tsv")
job = client.submit_job("SLE_status_3000", source="user_upload", input_file_key=key)
```

The matrix must be `.txt`/`.csv`/`.tsv`, under 2 GB, genes × samples, with **HGNC symbols** in the
gene column. Duplicate symbols — the ordinary result of an Ensembl→HGNC mapping — are collapsed by
**summing** their counts, the standard resolution for one gene measured across several loci; the
run reports how many rows were collapsed. Pre-aggregate yourself if you want different semantics.
Uploading requires a provisioned plan; self-serve accounts run the bundled reference datasets
(`source="reference_dataset"`).

### Order of operations

`wait_for_completion` returns as soon as the job is **COMPLETED** — proportions are ready at that
point. The R analysis stage (gene scores, plots) is triggered server-side and reaches
`R_COMPLETE`; pass `wait_for_analysis=True` to block for it, then collect the outputs:

```python
client.wait_for_completion(job_id, wait_for_analysis=True)
for f in client.list_files(job_id, category="r_outputs"):
    print(f["name"], f["url"])
```

The default timeout is **3 hours**, which the workload needs: the SLE reference cohort reaches
`R_COMPLETE` in ~26 minutes, but the 405-sample BRCA cohort takes over two hours because its R
stage runs subcluster differential expression across all 15 cell types. A `B2SCTimeout` does not
cancel the job — it carries `.elapsed` and `.last_status`, and you can keep polling `get_job()`.

### Removed in 0.2.0

The AI clinical report (an Opus-written PDF) was removed from the product on 2026-08-04, and with
it `generate_report()`, `wait_for_report()` and `download_report()`. The deliverable is the R stage
output — proportions, gene scores and plots — via `get_proportions()` and `list_files()`.

Calling a removed method raises `B2SCRemovedError`, which names the replacement and shows the
equivalent code. It subclasses both `B2SCError` and `AttributeError`, so `hasattr()` feature
detection correctly reports the method as absent while a direct call still explains itself.

The `wait_for_report=` **parameter** of `wait_for_completion` was only renamed, not removed — it
always waited for the R stage. It still works and warns.

Errors raise `B2SCError` (`.status`, `.code`); a poll timeout raises `B2SCTimeout`. A `409` from
`get_proportions` means the results are not produced yet — retry.

> **Data residency:** genomic expression data is sensitive. Analysis runs in AWS us-east-1;
> confirm that placement with WittGen before sending patient-derived data. Note the API does not
> send your data to any third-party model provider — the AI report that did was removed in 0.2.0.

## License

Apache-2.0 — see [LICENSE](LICENSE). This client SDK is open source; the WittGen B2SC **model
and service** it talks to remain proprietary. Copyright 2026 WittGen Biotechnologies.
