Metadata-Version: 2.4
Name: wittgen-b2sc
Version: 0.1.2
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 as a dataframe. 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
```

If that 404s, this build has not been published to PyPI yet — install the wheel we sent you:

```bash
pip install ./wittgen_b2sc-0.1.1-py3-none-any.whl
pip install pandas          # optional, for as_dataframe=True
```

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("breast_cancer_cell_type3", source="reference_dataset")
job_id = job["job_id"]

# 2. Wait for the generative pipeline, then for the R analysis stage
#    (a full run takes ~30 minutes — the GPU generates single cells from your bulk profile)
client.wait_for_completion(job_id, poll_interval=15, timeout=3600, wait_for_report=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. Ask for the AI clinical report, then wait for the PDF.
#    This step is explicit because it is the most expensive operation in the pipeline —
#    it is not generated unless you ask for it.
client.generate_report(job_id)
client.wait_for_report(job_id)
client.download_report(job_id, "report.pdf")
```

## Databricks quickstart

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

```python
# Cell 1 — install
# Until the package is on PyPI, upload the wheel to DBFS and install that instead:
#   %pip install /dbfs/FileStore/wittgen_b2sc-0.1.1-py3-none-any.whl
%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("breast_cancer_cell_type3", 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=3600, wait_for_report=False)` | Poll until COMPLETED (or, with `wait_for_report=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 |
| `generate_report(job_id, force=False)` | Start the AI clinical PDF (explicit, metered) |
| `wait_for_report(job_id, poll_interval=15, timeout=1800)` | Poll until the PDF is done |
| `get_report(job_id)` | Report status + presigned URL |
| `download_report(job_id, dest_path)` | Save the report PDF |

### 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_report=True` to block for it.

The AI clinical PDF is **not** automatic. It is the most expensive operation in the product, so you
ask for it explicitly once R has finished:

```python
client.wait_for_completion(job_id, wait_for_report=True)   # -> R_COMPLETE
client.generate_report(job_id)                             # starts the PDF
client.wait_for_report(job_id)                             # -> COMPLETE
client.download_report(job_id, "report.pdf")
```

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 / BAA:** genomic expression data is PHI. Confirm your BAA and the API
> region with WittGen before sending patient-derived data. See the data-residency one-pager.

## 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.
