hfx-tools
Tools for working with HFX submissions (Haplotype Frequency Exchange).
This repo provides composable command line tools and a Streamlit app for building, packing, inspecting, and validating HFX documents, implementing the HFX specification. Key features include:
build- Build HFX bundles from a folder with automatic validationpack- Pack HFX archives from metadata.json with optional manifests and checksumsqc- Compute quality control statisticsinspect- Inspect metadata or bundled HFX files- Validation framework - Extensible validation with built-in validators
- Streamlit UI - Web-based interface for building HFX files
Key schema facts
-
metadata.frequencyLocationcontrols where frequencies are stored: either"inline"or a URI (e.g.,file://frequencies.csv) (see HFX specification). -
If inline, the JSON may include
frequencyData(array of{haplotype, frequency}). -
metadata.frequencyFileHeadermaps CSV column names to the expectedhaplotype/frequencyfield names when the data file uses non-standard headers.
Install
Basic installation
pip install hfx-tools
With optional dependencies
# For Parquet support
pip install "hfx-tools[parquet]"
# For Streamlit web UI
pip install "hfx-tools[streamlit]"
Quick Start
5-minute walkthrough for the most common workflow:
# 1. Create input folder with metadata and data at top level
mkdir my_submission
cp my_metadata.json my_submission/
cp my_frequencies.csv my_submission/
# 2. Build and validate
hfx-build my_submission -n my_hfx_file
# 3. Done! Check output
ls -la my_submission/my_hfx_file.hfx
cat my_submission/my_hfx_file.build.log
For a guided interactive experience, launch the Streamlit web UI:
streamlit run "$(python -c 'import hfx_tools.streamlit_app as app; print(app.__file__)')"
Architecture
hfx-tools follows a layered architecture:
CLI / Streamlit UI (user-facing)
↓
build.py (orchestration)
↓
validators.py (validation rules) ← pack.py (packing logic)
↓
io.py (file I/O, JSON parsing)
- CLI layer (
cli.py) - Parses command-line arguments and delegates to build/pack/inspect/qc - Build orchestration (
build.py) - High-level workflow: reads metadata → detects files → validates → packs - Validation framework (
validators.py) - Pluggable validators for extensibility - Packing logic (
pack.py) - Low-level archive creation (ZIP with metadata, data, optional manifest) - I/O utilities (
io.py) - JSON parsing, file reading, consistent error handling
This design allows hackathon participants to:
- Use the CLI for quick workflows
- Call
build()directly from Python for programmatic use - Register custom validators without modifying core code
- Extend with custom QC statistics
Usage
Frequency Location Types
The HFX standard supports four types of frequency data locations:
- Inline -
"frequencyLocation": "inline"withfrequencyDataarray in same JSON - Remote -
"frequencyLocation": "https://zenodo.org/.../data.csv"or S3 URL - File (CSV) -
"frequencyLocation": "file://frequencies.csv"pointing to file within HFX bundle - File (Parquet) - Same as above but with
.parquetextension
CLI: Build from folder
The most common workflow:
hfx-build /path/to/input_folder -n output_name
Expected folder structure:
input_folder/
├── metadata.json # Required: HFX metadata (optionally with inline frequencyData)
└── frequencies.csv # Optional: if frequencyLocation = "file://frequencies.csv"
This will:
- Read
metadata.jsonfrominput_folder/ - Auto-detect a frequency data file in the same folder
- Auto-update
metadata.frequencyLocationtofile://<filename>(unless already set to remote or inline) - Validate all data with built-in validators
- Pack into a single
output_name.hfxfile - Log all validation results to
output_name.build.log
Example:
cp metadata.json example/
cp frequencies.csv example/
hfx-build example -n my_submission
# Output: example/my_submission.hfx
Options:
-n, --name NAME- Output filename (required, without .hfx)-o, --out DIR- Output directory (defaults to input folder)--no-manifest- Skip MANIFEST.json in archive--hash {md5,sha256,none}- Hash algorithm (default: sha256)--no-auto-update-location- Don't auto-updatemetadata.frequencyLocation(advanced)
CLI: Pack (low-level)
For direct packing when you already have a metadata.json:
hfx-pack metadata.json -o dist/example.hfx --manifest --hash sha256
CLI: Inspect
hfx-inspect metadata.json # Inspect a metadata.json file
hfx-inspect example.hfx # Inspect a bundled .hfx archive
hfx-tools inspect example.hfx # Equivalent generic command
CLI: QC
hfx-qc metadata.json --write-metadata --topk 10 100 1000
Streamlit: Web UI
Launch the interactive web interface:
streamlit run "$(python -c 'import hfx_tools.streamlit_app as app; print(app.__file__)')"
The Streamlit app provides:
- Folder browser - Select local folders containing metadata.json and data files
- File upload - Upload metadata.json and data files directly
- Auto-update mode - Automatically sets
metadata.frequencyLocationto point to uploaded data - Metadata preview - View JSON structure and what will be auto-updated before building
- Validation preview - Run validators and see results
- HFX download - Download the built .hfx file
- Build logs - View detailed validation and packing logs
- HFX inspector - Browse an existing
.hfxarchive and view its bundled metadata
For folder-based builds, the Output folder defaults to output, keeping generated archives
and build logs separate from example or source input folders.
GitHub sign-in (optional)
The Streamlit app always requires a contributor name, affiliation, and GitHub identity before building. GitHub usernames can be entered manually. To also offer Sign in with GitHub:
-
Create a GitHub OAuth App.
-
Set its authorization callback URL to your public Streamlit app URL.
-
Store these values in your deployment secrets, or in an untracked
.streamlit/secrets.tomlfor local use:[github_oauth] client_id = "..." client_secret = "..." redirect_uri = "https://your-app-url/"
OAuth is enabled only when all three values are present. It requests the read:user scope and
uses the authenticated GitHub login as the identity. Manual GitHub identity remains available
when OAuth is not configured. Never commit this secrets file.
Validation Framework
The build process includes an extensible validation framework with built-in validators:
- Schema version - Ensures top-level
versionmatches the current HFX schema (0.1.1) - Metadata required fields - Checks
outputResolution,hfeMethod,cohortDescription,nomenclatureUsed,frequencyLocation - Frequency location - Validates frequency location format (inline, file://, http://)
- Frequency data format - Checks inline frequency data structure, types, and duplicates
- File references - Verifies that referenced data files exist
Validation results are logged and returned with error/warning levels. The build fails if any error-level validations fail.
Custom validators
from hfx_tools.validators import ValidationFramework, ValidationResult
def my_custom_validator(metadata_json, hfx_obj, data_folder):
return ValidationResult(
validator_name="my_validator",
passed=True,
message="My validation passed",
level="info", # or "warning", "error"
)
validator_framework = ValidationFramework()
validator_framework.register_validator("my_validator", my_custom_validator)
Common Use Cases
Scenario 1: Batch submission from local folders
for dir in submissions/*/; do
hfx-build "$dir" -n "$(basename $dir)" -o dist/
done
Scenario 2: Remote frequency data
Point to frequencies hosted on Zenodo or S3 without bundling:
{
"frequencyLocation": "https://zenodo.org/record/12345/files/data.csv"
}
hfx-build my_submission -n my_file --no-auto-update-location
Scenario 3: Inline small frequencies
{
"frequencyLocation": "inline",
"frequencyData": [
{"haplotype": "A*01:01", "frequency": 0.123},
{"haplotype": "A*01:02", "frequency": 0.456}
]
}
Scenario 4: Non-standard CSV headers
If your CSV uses column names other than haplotype/frequency, map them in metadata:
{
"frequencyFileHeader": {
"Haplo": "haplotype",
"Freq": "frequency"
}
}
Scenario 5: Programmatic use in Python
from hfx_tools.build import build_hfx_from_folder
result = build_hfx_from_folder(
input_folder="my_data/",
output_name="my_submission",
output_dir="dist/",
hash_alg="sha256",
write_manifest=True,
)
print(f"Build {'succeeded' if result['success'] else 'failed'}")
for v in result["validation_results"]:
print(f" {v.level}: {v.message}")
Developer API
Using the Validation Framework
from hfx_tools.validators import ValidationFramework, ValidationResult
validator = ValidationFramework()
def check_cohort_size(metadata_json, hfx_obj, data_folder):
size = hfx_obj.get("metadata", {}).get("cohortDescription", {}).get("cohortSize", 0)
if size < 100:
return ValidationResult(
validator_name="cohort_size",
passed=False,
message=f"Cohort too small: {size} < 100",
level="warning",
)
return ValidationResult(
validator_name="cohort_size", passed=True, message=f"Cohort size OK: {size}", level="info"
)
validator.register_validator("cohort_size", check_cohort_size)
results = validator.validate(metadata_path, hfx_obj, data_folder)
Package contents
hfx_tools/
├── __init__.py
├── build.py # Build orchestration
├── cli.py # Command-line interface
├── inspect.py # HFX inspection tools
├── io.py # JSON and file I/O
├── pack.py # Low-level packing
├── qc.py # Quality control
├── streamlit_app.py # Web UI
├── util.py # Utilities
└── validators.py # Validation framework
Development & Contributing
Development setup
git clone https://github.com/societyforimmunepolymorphism/hfx-tools
cd hfx-tools
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --editable .
python -m pip install pytest ruff build
Running tests and linting
make fmt # Format code
make lint # Check code style
make test # Run test suite
make build # Build distribution
Submitting changes
- Fork the repo
- Create a feature branch (
git checkout -b feature/my-feature) - Add tests for new functionality
- Run
make lint testto verify - Submit a pull request
Troubleshooting
Issue: "Missing required field: metadata.frequencyLocation"
Add to your metadata.json:
{
"frequencyLocation": "file://frequencies.csv"
}
Or for inline data:
{
"frequencyLocation": "inline",
"frequencyData": [...]
}
Issue: Validation errors but can't see why
hfx-build my_data -n output
cat my_data/output.build.log
Issue: File not found in bundle
Ensure the filename in frequencyLocation matches the actual file in your folder:
my_data/
├── metadata.json # frequencyLocation: "file://my_file.csv"
└── my_file.csv # ← must match
Issue: CSV columns not recognized
Add frequencyFileHeader to your metadata to map your column names:
{
"frequencyFileHeader": {
"Haplo": "haplotype",
"Freq": "frequency"
}
}
Issue: Permission denied when creating .venv
mkdir -p ~/.hfx-tools
make sync VENV=~/.hfx-tools/.venv
Resources
- HFX Specification - Authoritative format specification and schema
- phycus - Related NMDP bioinformatics tools
- Issues & Discussions - Report bugs or suggest features
- HFX Spec Issues - Discuss spec-related questions
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hfx_tools-0.1.0.tar.gz.
File metadata
- Download URL: hfx_tools-0.1.0.tar.gz
- Upload date:
- Size: 31.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
565b3ee507481d16ca10e8cae4e648bc6c0a1fa4296ba76f42fcfa32364a4be3
|
|
| MD5 |
ff01287fbb5efac0dc5cbd0eaaa04158
|
|
| BLAKE2b-256 |
55b23deb93682586cf9cbb8f317509e05b4816a57da2abc526177050cd85b10b
|
Provenance
The following attestation bundles were made for hfx_tools-0.1.0.tar.gz:
Publisher:
publish.yml on societyforimmunepolymorphism/hfx-tools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hfx_tools-0.1.0.tar.gz -
Subject digest:
565b3ee507481d16ca10e8cae4e648bc6c0a1fa4296ba76f42fcfa32364a4be3 - Sigstore transparency entry: 2590438143
- Sigstore integration time:
-
Permalink:
societyforimmunepolymorphism/hfx-tools@6dfca24154fd78e643138405e8081b9c48f1c3f2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/societyforimmunepolymorphism
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6dfca24154fd78e643138405e8081b9c48f1c3f2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file hfx_tools-0.1.0-py3-none-any.whl.
File metadata
- Download URL: hfx_tools-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3cb8f9928203fc35d2ec4ae85a1d06ea29d11190048338fe77d35e9dfbaf011b
|
|
| MD5 |
dfad2de5583b1f5b34aaf9ecb7ca3625
|
|
| BLAKE2b-256 |
7836fca10d518ab97448cabed30773cb46b515a2b16e56985ffae3a526b1d8d9
|
Provenance
The following attestation bundles were made for hfx_tools-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on societyforimmunepolymorphism/hfx-tools
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hfx_tools-0.1.0-py3-none-any.whl -
Subject digest:
3cb8f9928203fc35d2ec4ae85a1d06ea29d11190048338fe77d35e9dfbaf011b - Sigstore transparency entry: 2590438557
- Sigstore integration time:
-
Permalink:
societyforimmunepolymorphism/hfx-tools@6dfca24154fd78e643138405e8081b9c48f1c3f2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/societyforimmunepolymorphism
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6dfca24154fd78e643138405e8081b9c48f1c3f2 -
Trigger Event:
release
-
Statement type: