| """Download a versioned scPerturb count matrix and verify its source checksum.""" |
|
|
| from pathlib import Path |
| import argparse, hashlib, os |
| import requests |
|
|
| FILES = { |
| "norman": ("NormanWeissman2019_filtered.h5ad", "c870e6967d91c017d9da827bab183cd6"), |
| "replogle_k562": ( |
| "ReplogleWeissman2022_K562_essential.h5ad", |
| "d8cba17576d1a8afc0f7d71b79cad0f7", |
| ), |
| } |
|
|
|
|
| def md5(path): |
| h = hashlib.md5() |
| with open(path, "rb") as f: |
| for block in iter(lambda: f.read(8 * 1024**2), b""): |
| h.update(block) |
| return h.hexdigest() |
|
|
|
|
| def download(dataset, output): |
| name, digest = FILES[dataset] |
| out = Path(output) |
| out.mkdir(parents=True, exist_ok=True) |
| dest = out / name |
| if dest.exists(): |
| if md5(dest) != digest: |
| raise ValueError(f"Checksum mismatch: {dest}") |
| return dest |
| part = dest.with_suffix(".h5ad.partial") |
| offset = part.stat().st_size if part.exists() else 0 |
| url = f"https://zenodo.org/records/10044268/files/{name}?download=1" |
| with requests.get( |
| url, |
| stream=True, |
| headers={"Range": f"bytes={offset}-"} if offset else {}, |
| timeout=(30, 120), |
| ) as response: |
| response.raise_for_status() |
| append = offset > 0 and response.status_code == 206 |
| with open(part, "ab" if append else "wb") as f: |
| for chunk in response.iter_content(8 * 1024**2): |
| f.write(chunk) |
| if md5(part) != digest: |
| raise ValueError(f"Source checksum mismatch: {part}") |
| os.replace(part, dest) |
| return dest |
|
|
|
|
| if __name__ == "__main__": |
| p = argparse.ArgumentParser() |
| p.add_argument("dataset", choices=FILES) |
| p.add_argument("--output", default="data/raw") |
| a = p.parse_args() |
| print(download(a.dataset, a.output)) |
|
|