Metadata-Version: 2.4
Name: pyxomiser
Version: 0.1.0
Summary: Python-native interface to Exomiser with Docker and Apptainer execution and pandas-based result handling.
Author: AthSre13
License-Expression: MIT
Project-URL: Homepage, https://github.com/AthSre13/PyXomiser
Project-URL: Repository, https://github.com/AthSre13/PyXomiser
Project-URL: Issues, https://github.com/AthSre13/PyXomiser/issues
Project-URL: Documentation, https://github.com/AthSre13/PyXomiser#readme
Keywords: Exomiser,genomics,rare disease,variant prioritization,HPO
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas>=2.0
Requires-Dist: pyarrow>=15.0
Requires-Dist: platformdirs>=4.0
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Provides-Extra: release
Requires-Dist: build>=1.2; extra == "release"
Requires-Dist: twine>=6.0; extra == "release"
Dynamic: license-file

# PyXomiser

PyXomiser is a Python interface to independently obtained Exomiser 15.1.0
software and reference data. It accepts a VCF plus HPO terms, or an existing
Phenopacket, and returns ranked results as pandas DataFrames.

The `0.1.0` target is an early research-software release. PyXomiser is not
clinically validated and is not intended by itself for clinical diagnosis or
medical decision-making.

```text
VCF + HPO terms / Phenopacket
              ↓
          PyXomiser
       Docker or Apptainer
              ↓
          Exomiser
              ↓
       pandas + Parquet
```

PyXomiser does not redistribute Exomiser, its container images, or its
reference datasets. It never downloads images/data, invokes `sudo`, changes
Unix groups, starts services, or writes to reference data.

## Installation

Python 3.10 or newer is required. The normal installation declares the full
runtime dependency set, including the Parquet engine:

```bash
python -m pip install .
```

After publication, the package-index installation will be:

```bash
python -m pip install pyxomiser
```

For development and tests:

```bash
python -m pip install -e ".[test]"
```

The mandatory runtime dependencies are `pandas`, `pyarrow`, and
`platformdirs`. No separate `pip install pyarrow` step is required.

## Choose an execution backend

Docker is convenient for workstations and local Linux/macOS environments.
Apptainer is intended for HPC/shared systems where users cannot access a
Docker daemon. An HPC administrator may need to provide Apptainer or a module
such as `module load apptainer`; cluster setup is site-specific.

### Docker prerequisites

Docker execution requires all three conditions:

1. the Docker CLI is installed;
2. the Docker daemon is running; and
3. the current user can access that daemon.

Installation commands vary by distribution; follow Docker's
[official Engine installation documentation](https://docs.docker.com/engine/install/)
for the host system.

Verify the connection before running PyXomiser:

```bash
docker info
```

On a systemd Linux installation, a user with appropriate administrative
privileges may need to start Docker externally:

```bash
sudo systemctl start docker
sudo systemctl enable docker       # optional
```

PyXomiser never runs these commands. It also never prefixes Docker commands
with `sudo`. On many Linux Docker Engine installations, non-root access can be
configured externally with:

```bash
sudo usermod -aG docker "$USER"
newgrp docker
docker run --rm hello-world
```

The `docker` group grants root-level privileges. Use Docker's
[rootless mode](https://docs.docker.com/engine/security/rootless/) if that is
preferable. If the CLI is installed but the current user cannot access the
daemon, `check_environment()` reports this separately from a stopped daemon.
If the daemon is running but the Exomiser image is absent, that is reported as
a missing local image. PyXomiser does not pull it.

Obtain the compatible image independently:

```text
exomiser/exomiser-cli:15.1.0-bash
```

### Apptainer prerequisites

Verify the runtime and provide a SIF independently:

```bash
apptainer --version
```

An administrator or user may create a SIF from the Docker/OCI image with an
external command such as:

```bash
apptainer pull exomiser-15.1.0.sif \
    docker://exomiser/exomiser-cli:15.1.0-bash
```

PyXomiser does not execute that pull or build. `backend="apptainer"` means
Apptainer specifically; detection of a `singularity` executable is reported
separately and does not claim compatibility.

Apptainer support is implemented and unit-tested, but this repository has not
validated it through a real Apptainer runtime.

## Reference data setup

PyXomiser does not download reference data. Obtain compatible Exomiser data
independently and pass the common root directory to `data_dir`:

```text
/path/to/exomiser-data/
├── 2406_phenotype/
│   ├── 2406_phenotype.mv.db
│   ├── hp.obo
│   ├── rw_string_10.mv
│   └── phenix/
└── 2406_hg19/
    ├── 2406_hg19_clinvar.mv.db
    ├── 2406_hg19_genome.mv.db
    ├── 2406_hg19_variants.mv.db
    ├── 2406_hg19_transcripts_ensembl.ser
    ├── 2406_hg19_transcripts_refseq.ser
    └── 2406_hg19_transcripts_ucsc.ser
```

These are versioned Exomiser resource directories with expected filenames;
they are not arbitrary two-file inputs. An `hg19` analysis requires phenotype
data plus a complete `hg19` resource. An `hg38` analysis requires phenotype
data plus a complete `hg38` resource.

`2406` is an example/current tested release, not a universal hard-coded
requirement. Phenotype, hg19, and hg38 versions are discovered independently.
The resolver also detects an observed nested form such as
`2406_hg19/2406_hg19/`, mounts the physical payload correctly, and reports a
nonstandard-layout warning. The standard layout above is recommended.

Inspect setup without modifying data:

```python
import pyxomiser as px

print(px.data_status(data_dir="/path/to/exomiser-data"))
print(px.validate_data("/path/to/exomiser-data", assembly="hg19").as_dict())
print(px.check_environment(data_dir="/path/to/exomiser-data", assembly="hg19"))
```

## Configuration and analysis

```python
import pyxomiser as px

px.configure(
    backend="docker",
    data_dir="/path/to/exomiser-data",
)

result = px.run_from_hpo(
    vcf="patient.vcf.gz",
    hpo_terms=["HP:0001250", "HP:0001263"],
    assembly="hg19",
)
```

For HPC:

```python
px.configure(
    backend="apptainer",
    apptainer_image="/shared/containers/exomiser-15.1.0.sif",
    data_dir="/shared/exomiser-data",
)

result = px.run_from_hpo(
    vcf="/scratch/user/patient.vcf.gz",
    hpo_terms=["HP:0001250"],
    assembly="hg19",
)
```

The same `run`, `run_from_hpo`, and `batch_run` APIs work with either backend.
Reference mounts are read-only; input mounts are read-only; workspace output
is writable. Arbitrary absolute paths such as `/project/group`, `/scratch`,
and `/shared/reference` are supported.

Settings can be persisted with `persist=True`. Precedence for run-time
configuration is:

```text
explicit arguments > environment variables > persistent configuration > defaults
```

Relevant variables include `EXOMISER_BACKEND`, `EXOMISER_APPTAINER_IMAGE`,
`EXOMISER_DATA_DIR`, `EXOMISER_DOCKER_IMAGE`, `EXOMISER_MEMORY`, and the
independent data-version variables.

Use `dry_run=True` to inspect the backend command without executing it:

```python
plan = px.run_from_hpo(
    vcf="patient.vcf.gz",
    hpo_terms=["HP:0001250"],
    assembly="hg19",
    backend="apptainer",
    apptainer_image="/shared/containers/exomiser-15.1.0.sif",
    dry_run=True,
)
print(plan.as_dict())
```

The plan includes the backend executable, SIF path where applicable, logical
bind mounts, environment, Exomiser arguments, and output location.

## Results

`result.genes` and `result.variants` remain ordinary pandas DataFrames:

```python
result.genes.head()
result.variants.head()

result.top_genes(20)
result.top_variants(20)
result.gene("FGFR2")
result.gene("FGFR2", moi="AD")
result.variants_for_gene("FGFR2")
result.variants_for_gene("FGFR2", moi="AD")
result.by_moi("AD")
result.contributing_variants()
result.variants_by_acmg("PATHOGENIC")
```

The helper methods return copies and preserve Exomiser's ranking/order. A
gene lookup without `moi` returns all matching GeneScores; it never silently
chooses one MOI. `by_moi()` returns a new `ExomiserResult` containing both
filtered tables, using the exact MOI values emitted by Exomiser.

Gene rows represent one `(geneSymbol, moi)` GeneScore. Variant rows represent
one variant–gene–MOI association and are deliberately not globally
deduplicated. `contributing_variants()` uses Exomiser's
`isContributingVariant` field. `variants_by_acmg()` performs exact ACMG class
matching; `PATHOGENIC` and `LIKELY_PATHOGENIC` are not merged.

Because these are pandas objects, normal operations require no export wrapper:

```python
result.genes.query("geneCombinedScore > 0.7")
result.genes.to_csv("genes.csv", index=False)
result.variants.groupby("geneSymbol").size()
```

`repr(result)` is intentionally concise and excludes patient identifiers:

```text
ExomiserResult(gene_scores=256, variant_associations=300, assembly='hg19', backend='docker')
```

Raw output files can be preserved with `output_dir` or `keep_files=True` and
are available through `result.files`.

## Provenance and reproducibility

Inspect `result.metadata` for run provenance. Depending on the backend and
execution path it includes the PyXomiser version, Exomiser version, backend,
container image or SIF path, image digest when Docker reports one, phenotype
and assembly data versions, assembly, preset or custom-analysis usage,
output formats, and a UTC timestamp. External containers, reference data, and
analysis inputs remain outside PyXomiser's control, so this metadata supports
reproducibility but cannot guarantee it when those resources change.

## Batch processing

```python
batch = px.batch_run(
    samples=[
        {"id": "patient1", "vcf": "patient1.vcf.gz", "hpo_terms": ["HP:0001250"]},
        {"id": "patient2", "vcf": "patient2.vcf.gz", "hpo_terms": ["HP:0001263"]},
    ],
    assembly="hg19",
    backend="apptainer",
    apptainer_image="/shared/containers/exomiser-15.1.0.sif",
    max_workers=2,
)
```

Batch execution starts local backend processes with bounded `max_workers`; it
does not submit Slurm/PBS jobs.

## Validation and testing

Environment validation is read-only and structured:

```python
status = px.check_environment(backend="apptainer", assembly="hg19")
print(status.as_dict())       # machine-readable
print(status)                 # human-readable
```

Run the repository tests with:

```bash
python -m pytest -q tests/unit
```

The real Pfeiffer Docker regression is opt-in because it requires the
independently obtained image, data, and Docker runtime:

```bash
PYXOMISER_RUN_INTEGRATION=1 python -m pytest -q tests/integration
```

The expected top-ranked gene is `FGFR2`. Apptainer command construction and
backend behavior are unit-tested. A real Apptainer Pfeiffer run requires a
local SIF and is not claimed unless it has been run in the target environment.

## Examples

- [`examples/basic_hpo.py`](examples/basic_hpo.py) — VCF plus HPO terms.
- [`examples/phenopacket.py`](examples/phenopacket.py) — existing Phenopacket.
- [`examples/batch.py`](examples/batch.py) — bounded batch execution.

## Scope and licensing

PyXomiser is an independent MIT-licensed project; see [`LICENSE`](LICENSE).
It does not distribute Exomiser, Exomiser container images, Apptainer/SIF
images, or Exomiser reference data. Obtain those external components
separately under their respective terms. Exomiser has its own
[official project](https://github.com/exomiser/Exomiser) and
[license](https://github.com/exomiser/Exomiser/blob/master/LICENCE); the
PyXomiser MIT license applies only to this repository and does not grant
rights to Exomiser or its datasets. PyXomiser is not affiliated with or
endorsed by the Exomiser authors.
