Skip to content

Commit 605f8a6

Browse files
authored
Merge pull request #180 from MIT-LCP/tp/download
Add simple dataset download support
2 parents f63c7ee + 819bbcf commit 605f8a6

6 files changed

Lines changed: 1126 additions & 4 deletions

File tree

README.md

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,61 @@ Requires Python 3.9 or later.
1515
The package provides a `physionet` command-line tool. You can also run it as a
1616
module with `python -m physionet`.
1717

18+
### `physionet download`
19+
20+
Download datasets from PhysioNet:
21+
22+
```bash
23+
# Download the latest version of a dataset
24+
physionet download mimic-iv-demo
25+
26+
# Download a specific version
27+
physionet download mimic-iv-demo --version 2.2
28+
29+
# Download to a specific directory
30+
physionet download mimic-iv-demo --output /data
31+
32+
# Preview what would be downloaded
33+
physionet download mimic-iv-demo --dry-run
34+
35+
# Download only specific files
36+
physionet download mimic-iv-demo --include "*.csv" --exclude "*/notes/*"
37+
```
38+
39+
**Download sources:**
40+
41+
The `--source` flag controls where files are downloaded from:
42+
43+
- `auto` (default) — tries S3 first, falls back to PhysioNet direct if the dataset is not available on S3
44+
- `physionet` — always downloads from PhysioNet directly
45+
- `aws` — downloads from S3 using boto3 and the standard AWS credential chain
46+
47+
```bash
48+
# Download from PhysioNet directly
49+
physionet download mimic-iv-demo --source physionet
50+
51+
# Download from S3 using boto3
52+
physionet download mimic-iv-demo --source aws
53+
```
54+
55+
When using `--source aws`, boto3 discovers credentials automatically via the [standard AWS credential chain](https://docs.aws.amazon.com/sdkref/latest/guide/standardized-credentials.html) (environment variables, `~/.aws/credentials`, IAM roles, etc.). This allows downloading credentialed datasets from S3 without passing PhysioNet credentials.
56+
57+
**Authentication:**
58+
59+
For credentialed datasets, provide PhysioNet credentials via flags or environment variables:
60+
61+
```bash
62+
# Via flags
63+
physionet download mimic-iv --username user --password pass
64+
65+
# Via environment variables
66+
export PHYSIONET_USERNAME=user
67+
export PHYSIONET_PASSWORD=pass
68+
physionet download mimic-iv
69+
```
70+
71+
Downloads support automatic resume, SHA256 checksum verification, and retry on transient errors.
72+
1873
### `physionet validate`
1974

2075
Validate a dataset before submission to PhysioNet. The validator checks for
@@ -72,9 +127,21 @@ physionet validate /path/to/dataset --max-rows 5000
72127
- `0` - Validation passed (no errors).
73128
- `1` - Validation failed with errors.
74129

75-
### Validation from Python
130+
## Python API
131+
132+
### Download
133+
134+
```python
135+
from physionet.download import download
136+
137+
# Download a dataset
138+
download("mimic-iv-demo", version="2.2", output_dir="/data")
139+
140+
# Download from S3 using boto3
141+
download("mimic-iv-demo", source="aws")
142+
```
76143

77-
The validator can also be used as a Python library:
144+
### Validation
78145

79146
```python
80147
from physionet import validate_dataset, ValidationConfig
@@ -101,7 +168,7 @@ print(result.summary())
101168
data = result.to_dict()
102169
```
103170

104-
## API Client
171+
### API Client
105172

106173
Interact with the PhysioNet REST API to explore and search published projects:
107174

physionet/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from physionet.api import PhysioNetClient
22
from physionet.validate import validate_dataset, ValidationConfig, ValidationResult
3+
from physionet.download import download
34

45
try:
56
from importlib.metadata import version
@@ -12,4 +13,5 @@
1213
"validate_dataset",
1314
"ValidationConfig",
1415
"ValidationResult",
16+
"download",
1517
]

physionet/cli.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,54 @@ def main():
1717

1818
subparsers = parser.add_subparsers(dest="command", help="Available commands")
1919

20+
# Download subcommand
21+
download_parser = subparsers.add_parser(
22+
"download",
23+
help="Download a dataset from PhysioNet",
24+
)
25+
download_parser.add_argument(
26+
"slug",
27+
help="Project identifier (e.g., mimic-iv-demo)",
28+
)
29+
download_parser.add_argument(
30+
"--version",
31+
help="Project version to download (default: latest)",
32+
)
33+
download_parser.add_argument(
34+
"--output",
35+
default=".",
36+
help="Output directory (default: current directory)",
37+
)
38+
download_parser.add_argument(
39+
"--include",
40+
action="append",
41+
help="Glob pattern for files to include (can be repeated)",
42+
)
43+
download_parser.add_argument(
44+
"--exclude",
45+
action="append",
46+
help="Glob pattern for files to exclude (can be repeated)",
47+
)
48+
download_parser.add_argument(
49+
"--source",
50+
choices=["auto", "physionet", "aws"],
51+
default="auto",
52+
help="Download source (default: auto)",
53+
)
54+
download_parser.add_argument(
55+
"--dry-run",
56+
action="store_true",
57+
help="Show what would be downloaded without downloading",
58+
)
59+
download_parser.add_argument(
60+
"--username",
61+
help="PhysioNet username (or set PHYSIONET_USERNAME env var)",
62+
)
63+
download_parser.add_argument(
64+
"--password",
65+
help="PhysioNet password (or set PHYSIONET_PASSWORD env var)",
66+
)
67+
2068
# Validate subcommand
2169
validate_parser = subparsers.add_parser(
2270
"validate",
@@ -56,7 +104,9 @@ def main():
56104

57105
args = parser.parse_args()
58106

59-
if args.command == "validate":
107+
if args.command == "download":
108+
return _handle_download(args)
109+
elif args.command == "validate":
60110
return _handle_validate(args)
61111
elif args.command is None:
62112
parser.print_help()
@@ -66,6 +116,39 @@ def main():
66116
return 1
67117

68118

119+
def _handle_download(args):
120+
"""Handle the download subcommand."""
121+
from physionet.download import download
122+
from physionet.api.exceptions import ForbiddenError, NotFoundError
123+
124+
try:
125+
download(
126+
slug=args.slug,
127+
version=args.version,
128+
output_dir=args.output,
129+
include=args.include,
130+
exclude=args.exclude,
131+
source=args.source,
132+
dry_run=args.dry_run,
133+
username=args.username,
134+
password=args.password,
135+
)
136+
return 0
137+
except NotFoundError as e:
138+
print(f"Error: {e}", file=sys.stderr)
139+
return 1
140+
except ForbiddenError as e:
141+
print(f"Access denied: {e}", file=sys.stderr)
142+
print(
143+
f"Visit https://physionet.org/content/{args.slug}/ to request access.",
144+
file=sys.stderr,
145+
)
146+
return 1
147+
except Exception as e:
148+
print(f"Error: {e}", file=sys.stderr)
149+
return 1
150+
151+
69152
def _handle_validate(args):
70153
"""Handle the validate subcommand."""
71154
# Validate path

0 commit comments

Comments
 (0)