dratools 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +7 -7
- data/docs/design.md +21 -7
- data/docs/environment.md +37 -15
- data/docs/usage.md +18 -17
- data/lib/dratools/accession_resolver.rb +27 -0
- data/lib/dratools/accession_resource_type_classifier.rb +2 -2
- data/lib/dratools/command_line_interface.rb +22 -11
- data/lib/dratools/commands/meta_command.rb +2 -2
- data/lib/dratools/commands/runs_command.rb +1 -25
- data/lib/dratools/commands/size_command.rb +10 -8
- data/lib/dratools/commands/url_command.rb +8 -7
- data/lib/dratools/config.rb +18 -4
- data/lib/dratools/ddbj_record_fields.rb +1 -6
- data/lib/dratools/ddbj_resource_client.rb +109 -22
- data/lib/dratools/download_candidate_builder.rb +4 -31
- data/lib/dratools/external_command_runner.rb +95 -12
- data/lib/dratools/progress_reporter.rb +87 -0
- data/lib/dratools/run_record_collector.rb +73 -26
- data/lib/dratools/version.rb +1 -1
- data/lib/dratools.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 580e0395df351cc56d957e756ad8368b663d9902f4722983e268093e97ec0809
|
|
4
|
+
data.tar.gz: 39206bc915245cea263f476cf7e5397f7e1027c1c21de3500e0c0c573f1c2c41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 91bfee6e903c4334818f4dce7da9a30df66db16412cefcfa91ed384e5e0f031acedb69d1f17a6765fd86602072219c5e10a80043ced268d9b966779673c3fef0
|
|
7
|
+
data.tar.gz: 000f9ca102c3deacd8ae0b1c1d9f8edea16b596f65383c3f0fb925f413ca773390e0d1a7e9615f728b6f8b9d41afe7d5b8cdc0be4814cb319af34ce61fea1dd5
|
data/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# dratools
|
|
2
2
|
|
|
3
3
|
[](https://github.com/kojix2/dratools/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/rb/dratools)
|
|
4
5
|
[](https://tokei.kojix2.net/github/kojix2/dratools)
|
|
6
|
+
[](https://doi.org/10.5281/zenodo.20967539)
|
|
5
7
|
|
|
6
8
|
[dratools](https://github.com/kojix2/dratools) は、日本国内の [DDBJ](https://www.ddbj.nig.ac.jp) からゲノムデータをダウンロードするためのツールです。
|
|
7
9
|
|
|
@@ -13,7 +15,7 @@ dratools は非公式のツールです。DDBJ や国立遺伝学研究所が提
|
|
|
13
15
|
|
|
14
16
|
必要なものは次のとおりです。
|
|
15
17
|
|
|
16
|
-
- [ruby](https://www.ruby-lang.org/)
|
|
18
|
+
- [ruby](https://www.ruby-lang.org/) 3.0 以上
|
|
17
19
|
- [curl](https://curl.se/) または [wget](https://www.gnu.org/software/wget/)
|
|
18
20
|
|
|
19
21
|
Rubyのgemとしてインストールできます。
|
|
@@ -80,7 +82,7 @@ printf 'DRR000001\nDRR000002\n' | dratools get -O ~/Downloads
|
|
|
80
82
|
dratools probe DRR000001 # URL の到達性だけ確認する
|
|
81
83
|
dratools url --json DRR000001 # URL 情報を JSON で表示する
|
|
82
84
|
dratools url --tsv DRR000001 # run/type/url/size/md5 を TAB 区切りで
|
|
83
|
-
dratools meta --json DRR000001 #
|
|
85
|
+
dratools meta --json DRR000001 # entry JSON を表示する
|
|
84
86
|
dratools runs PRJNA341783 | dratools get -O ~/Downloads # run 一覧をダウンロードへ渡す
|
|
85
87
|
dratools size --bytes PRJNA341783 # 合計サイズをバイト数で表示する
|
|
86
88
|
dratools size --per-run DRX000001 # 親 accession を run ごとに集計する
|
|
@@ -89,7 +91,7 @@ dratools get --skip-existing -O ~/Downloads DRR000001 # 既存ファイルは
|
|
|
89
91
|
|
|
90
92
|
詳しくは [使い方](docs/usage.md) をご覧ください。親 accession を扱うときの設定は [環境変数](docs/environment.md) にまとめています。
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
実際のファイル転送には通常 `curl` または `wget` を使います。`aria2c` も環境変数で明示した場合だけ使えます。ダウンロード中は進捗表示を端末にそのまま流します。同名のファイルが既にある場合は、サーバのファイルサイズと比べて再取得の要否を判断します。md5 が得られる場合は md5 で照合します。
|
|
93
95
|
|
|
94
96
|
ツールは全てコーディングエージェントによって実装されました。
|
|
95
97
|
|
|
@@ -102,11 +104,9 @@ dratools get --skip-existing -O ~/Downloads DRR000001 # 既存ファイルは
|
|
|
102
104
|
|
|
103
105
|
## お役立ちノート
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
日本国内のサーバーからゲノムデータをダウンロードすると作業が楽になります。
|
|
107
|
+
海外のサーバーからゲノムデータをダウンロードするのは大変です。日本国内のサーバーを使うと作業が楽になります。
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
NASの付属ソフトにURLを入力すると、何もしなくても自動でダウンロードが進むのでストレスが少ないです。
|
|
109
|
+
手間をかけずにダウンロードする方法として、NAS を使う方法があります。まず `dratools url` で URL の一覧を出します。次に NAS の付属ソフトの GUI 画面に、その URL をまとめて貼り付けます。あとは放置します。ダウンロードは自動で進みます。
|
|
110
110
|
|
|
111
111
|
## 開発
|
|
112
112
|
|
data/docs/design.md
CHANGED
|
@@ -12,24 +12,33 @@ accession から URL までの流れは次のとおりです。
|
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
14
|
accession
|
|
15
|
-
-> DDBJ Search
|
|
15
|
+
-> DDBJ Search entry JSON (/search/api/entries/{type}/{id}.json)
|
|
16
16
|
-> sra-run record
|
|
17
|
-
->
|
|
17
|
+
-> distribution
|
|
18
18
|
-> https / ftp URL
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
`/resource/{type}/{id}.json` は DDBJ Search の後方互換入口です。現在は
|
|
22
|
+
`/search/entry/{type}/{id}.json` へリダイレクトされ、さらに nginx で
|
|
23
|
+
`/search/api/entries/{type}/{id}.json` へ rewrite されます。dratools は
|
|
24
|
+
リダイレクトを避けるため、正規の `/search/api/entries` endpoint を直接使います。
|
|
25
|
+
|
|
26
|
+
run 一覧だけが必要な場合は DBLinks endpoint を使います。多数の run record が必要な
|
|
27
|
+
場合は Bulk endpoint を使います。どちらも同じ DDBJ Search API サーバーの機能です。
|
|
28
|
+
|
|
21
29
|
## ダウンロード確認
|
|
22
30
|
|
|
23
31
|
ゲノムデータはサイズが大きいです。`probe` は完全なダウンロードを行いません。
|
|
24
32
|
|
|
25
33
|
- `curl` がある場合: `--range 0-0` と `--max-time` を使う
|
|
26
34
|
- `wget` がある場合: `--spider` と `--timeout` を使う
|
|
35
|
+
- `aria2c` がある場合: `--dry-run=true` と `--timeout` を使う
|
|
27
36
|
|
|
28
37
|
これは URL が使えるかどうかを短時間で確認するためです。完全な整合性の確認ではありません。
|
|
29
38
|
|
|
30
39
|
## サイズ確認
|
|
31
40
|
|
|
32
|
-
`size` は
|
|
41
|
+
`size` は entry JSON のサイズ情報を使いません。実レコードにはサイズや md5 が含まれないことが多いためです。代わりに、解決した URL に HTTP `HEAD` を送ります。`Content-Length` を合計します。
|
|
33
42
|
|
|
34
43
|
FASTQ はディレクトリ URL で返ることがあります。その場合はディレクトリ一覧を取得します。`*.fastq*` のリンクを取り出します。各ファイルに `HEAD` を送ります。取得できないものは失敗にしません。`unresolved` として数えます。
|
|
35
44
|
|
|
@@ -37,12 +46,17 @@ FASTQ はディレクトリ URL で返ることがあります。その場合は
|
|
|
37
46
|
|
|
38
47
|
## 実ダウンロード
|
|
39
48
|
|
|
40
|
-
実ダウンロードでは総時間の上限を設けません。数十 GB のファイルでは長時間かかるためです。代わりに、接続のタイムアウトと失速検知を `curl` / `wget` に渡します。
|
|
49
|
+
実ダウンロードでは総時間の上限を設けません。数十 GB のファイルでは長時間かかるためです。代わりに、接続のタイムアウトと失速検知を `curl` / `wget` / `aria2c` に渡します。
|
|
41
50
|
|
|
42
51
|
- `curl`: `--connect-timeout`, `--speed-limit`, `--speed-time`, `--retry`
|
|
43
52
|
- `wget`: `--connect-timeout`, `--read-timeout`, `--tries`, `--waitretry`
|
|
53
|
+
- `aria2c`: `--connect-timeout`, `--timeout`, `--lowest-speed-limit`, `--max-tries`, `--retry-wait`
|
|
54
|
+
|
|
55
|
+
`aria2c` は分割ダウンロードができますが、既定では `--split=1` と `--max-connection-per-server=1` で単一接続にします。公共アーカイブへの負荷を既定で増やさないためです。
|
|
56
|
+
|
|
57
|
+
`DRATOOLS_DOWNLOAD_RETRY_COUNT` はリトライ回数として扱います。`curl --retry` はリトライ回数ですが、`wget --tries` と `aria2c --max-tries` は総試行回数なので、外部コマンドには `DRATOOLS_DOWNLOAD_RETRY_COUNT + 1` を渡します。
|
|
44
58
|
|
|
45
|
-
ダウンロードは `system(*command)` で実行します。`Open3.capture3`
|
|
59
|
+
ダウンロードは `system(*command)` で実行します。`Open3.capture3` は使いません。外部コマンドの進捗を端末にそのまま表示するためです。失敗した場合は、コマンド行と終了ステータスを `CommandError` にします。
|
|
46
60
|
|
|
47
61
|
## チェックサムと既存ファイル
|
|
48
62
|
|
|
@@ -53,7 +67,7 @@ md5 が得られる候補では、ダウンロード後に `Digest::MD5.file`
|
|
|
53
67
|
1. `--force` があれば既存ファイルを使わず再取得する
|
|
54
68
|
2. `--skip-existing` があれば md5 を見ずに既存ファイルを使う
|
|
55
69
|
3. md5 があり、既存ファイルの md5 が一致すれば再取得せず `Skipped` にする
|
|
56
|
-
4. それ以外は `curl --continue-at
|
|
70
|
+
4. それ以外は `curl --continue-at -`, `wget --continue`, `aria2c --continue=true` でレジュームを試みる
|
|
57
71
|
|
|
58
72
|
md5 が無い候補では、既定では既存ファイルをスキップしません。SRA ファイルとしての検証は dratools の既定動作には含めません。
|
|
59
73
|
|
|
@@ -69,7 +83,7 @@ Ruby 側の依存は増やしません。次の標準ライブラリを使いま
|
|
|
69
83
|
- `digest/md5`
|
|
70
84
|
- `minitest`
|
|
71
85
|
|
|
72
|
-
|
|
86
|
+
実ファイルの転送だけは外部コマンドに任せます。自動選択では環境にある `curl` または `wget` を使います。`aria2c` は `DRATOOLS_DOWNLOAD_COMMAND=aria2c` で明示指定された場合だけ使います。
|
|
73
87
|
|
|
74
88
|
## 今後の候補
|
|
75
89
|
|
data/docs/environment.md
CHANGED
|
@@ -6,45 +6,53 @@
|
|
|
6
6
|
|
|
7
7
|
| 環境変数 | 既定値 | 役割 |
|
|
8
8
|
| --- | ---: | --- |
|
|
9
|
-
| `DRATOOLS_MAX_RECURSIVE_NON_RUN_XREFS` | `
|
|
10
|
-
| `DRATOOLS_TREE_MAX_DIRECT_RUNS` | `
|
|
11
|
-
| `DRATOOLS_URL_MAX_DIRECT_RUNS` | `
|
|
12
|
-
| `DRATOOLS_SIZE_MAX_DIRECT_RUNS` | `
|
|
9
|
+
| `DRATOOLS_MAX_RECURSIVE_NON_RUN_XREFS` | `500` | `runs` などが direct run を持たない親レコードから experiment/sample/study などの非 run レコードを再帰的に辿る最大件数 |
|
|
10
|
+
| `DRATOOLS_TREE_MAX_DIRECT_RUNS` | `200` | `tree` が direct run レコードを個別取得して URL まで展開する最大 run 件数。超えた場合は件数だけを要約表示 |
|
|
11
|
+
| `DRATOOLS_URL_MAX_DIRECT_RUNS` | `200` | `url` が 1 つの親 accession から direct run を暗黙展開して URL を解決する最大 run 件数 |
|
|
12
|
+
| `DRATOOLS_SIZE_MAX_DIRECT_RUNS` | `200` | `size` が 1 つの親 accession から direct run を暗黙展開して HEAD する最大 run 件数 |
|
|
13
13
|
|
|
14
14
|
`unlimited` が使えるのは、上の 4 つの上限設定だけです。
|
|
15
15
|
|
|
16
16
|
## ダウンロード開始と失速検知
|
|
17
17
|
|
|
18
|
-
`get` は大きいファイルを扱います。このため、総ダウンロード時間の上限を設けません。代わりに、接続タイムアウト、失速検知、リトライの設定を `curl` / `wget` に渡します。
|
|
18
|
+
`get` は大きいファイルを扱います。このため、総ダウンロード時間の上限を設けません。代わりに、接続タイムアウト、失速検知、リトライの設定を `curl` / `wget` / `aria2c` に渡します。
|
|
19
19
|
|
|
20
20
|
| 環境変数 | 既定値 | 役割 |
|
|
21
21
|
| --- | ---: | --- |
|
|
22
22
|
| `DRATOOLS_DOWNLOAD_CONNECT_TIMEOUT` | `30` | 接続確立のタイムアウト秒数 |
|
|
23
23
|
| `DRATOOLS_DOWNLOAD_STALL_TIMEOUT` | `60` | この秒数のあいだ転送速度が閾値を下回ると失速扱いにする |
|
|
24
|
-
| `DRATOOLS_DOWNLOAD_STALL_SPEED` | `1024` |
|
|
25
|
-
| `DRATOOLS_DOWNLOAD_RETRY_COUNT` | `3` | ダウンロード失敗時のリトライ回数。`0`
|
|
26
|
-
| `DRATOOLS_DOWNLOAD_RETRY_WAIT` | `5` | `wget` のリトライ待ち秒数 |
|
|
24
|
+
| `DRATOOLS_DOWNLOAD_STALL_SPEED` | `1024` | `curl` / `aria2c` の失速判定に使う最低転送速度。単位は bytes/sec |
|
|
25
|
+
| `DRATOOLS_DOWNLOAD_RETRY_COUNT` | `3` | ダウンロード失敗時のリトライ回数。`0` はリトライなし |
|
|
26
|
+
| `DRATOOLS_DOWNLOAD_RETRY_WAIT` | `5` | `wget` / `aria2c` のリトライ待ち秒数 |
|
|
27
|
+
| `DRATOOLS_DOWNLOAD_COMMAND` | 自動 | 実ダウンロードと `probe` に使う外部コマンド。`curl`, `wget`, `aria2c` のいずれか |
|
|
28
|
+
|
|
29
|
+
`DRATOOLS_DOWNLOAD_COMMAND` を指定しない場合は、`curl`, `wget` の順で PATH にあるものを使います。`aria2c` は自動探索しません。`aria2c` を使う場合は `DRATOOLS_DOWNLOAD_COMMAND=aria2c` を指定してください。指定した場合はそのコマンドだけを使います。見つからない場合は、別のコマンドへ自動では切り替えません。
|
|
30
|
+
|
|
31
|
+
`DRATOOLS_DOWNLOAD_RETRY_COUNT` は、初回の試行を含まない「リトライ回数」です。`curl` にはそのまま渡します。`wget` と `aria2c` は総試行回数を受け取るため、内部で `リトライ回数 + 1` に変換します。
|
|
27
32
|
|
|
28
33
|
## 例
|
|
29
34
|
|
|
30
|
-
`tree`
|
|
35
|
+
`tree` の direct run 展開の既定値は 200 件です。
|
|
36
|
+
500 件に上げる例:
|
|
31
37
|
|
|
32
38
|
```sh
|
|
33
|
-
DRATOOLS_TREE_MAX_DIRECT_RUNS=
|
|
39
|
+
DRATOOLS_TREE_MAX_DIRECT_RUNS=500 dratools tree PRJDB12740
|
|
34
40
|
```
|
|
35
41
|
|
|
36
42
|
この値を小さくすると、展開しない direct run は要約だけを表示します。
|
|
37
43
|
|
|
38
|
-
`size`
|
|
44
|
+
`size` の direct run 暗黙展開の既定値は 200 件です。
|
|
45
|
+
500 件に上げる例:
|
|
39
46
|
|
|
40
47
|
```sh
|
|
41
|
-
DRATOOLS_SIZE_MAX_DIRECT_RUNS=
|
|
48
|
+
DRATOOLS_SIZE_MAX_DIRECT_RUNS=500 dratools size PRJDB12740
|
|
42
49
|
```
|
|
43
50
|
|
|
44
|
-
`url`
|
|
51
|
+
`url` の direct run 暗黙展開の既定値は 200 件です。
|
|
52
|
+
500 件に上げる例:
|
|
45
53
|
|
|
46
54
|
```sh
|
|
47
|
-
DRATOOLS_URL_MAX_DIRECT_RUNS=
|
|
55
|
+
DRATOOLS_URL_MAX_DIRECT_RUNS=500 dratools url --tsv PRJDB12740
|
|
48
56
|
```
|
|
49
57
|
|
|
50
58
|
再帰的な非 run 展開の上限を外す:
|
|
@@ -62,10 +70,24 @@ DRATOOLS_DOWNLOAD_RETRY_COUNT=0 \
|
|
|
62
70
|
dratools get --no-verify -O ~/Downloads DRR000001
|
|
63
71
|
```
|
|
64
72
|
|
|
73
|
+
`wget` を明示してダウンロードする:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
DRATOOLS_DOWNLOAD_COMMAND=wget dratools get -O ~/Downloads DRR000001
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`aria2c` を明示してダウンロードする:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
DRATOOLS_DOWNLOAD_COMMAND=aria2c dratools get -O ~/Downloads DRR000001
|
|
83
|
+
```
|
|
84
|
+
|
|
65
85
|
## 注意
|
|
66
86
|
|
|
67
87
|
これらは上級の設定です。上限を大きくすると、DDBJ Search API へのリクエスト数が増えます。`size` の HTTP `HEAD` の回数も増えます。
|
|
68
88
|
|
|
69
|
-
`DRATOOLS_URL_MAX_DIRECT_RUNS` と `DRATOOLS_SIZE_MAX_DIRECT_RUNS` は direct run 数の上限です。experiment や sample や study を経由して見つかる run
|
|
89
|
+
`DRATOOLS_URL_MAX_DIRECT_RUNS` と `DRATOOLS_SIZE_MAX_DIRECT_RUNS` は direct run 数の上限です。experiment や sample や study を経由して見つかる run の総数は制限しません。`*_MAX_DIRECT_RUNS=unlimited` だけでは `DRATOOLS_MAX_RECURSIVE_NON_RUN_XREFS` の上限は外れません。
|
|
90
|
+
|
|
91
|
+
まず `meta` や `tree` で構造を確認してください。必要なら `runs` で accession を絞ってください。その後で重い操作を実行してください。
|
|
70
92
|
|
|
71
93
|
ダウンロード用の設定を小さくしすぎる場合を考えます。正常なサーバーでも、開始前や転送中に失敗します。短い値は動作検証やネットワーク問題の切り分けに使ってください。通常のダウンロードでは既定値を使ってください。
|
data/docs/usage.md
CHANGED
|
@@ -35,7 +35,7 @@ bundle exec rake install
|
|
|
35
35
|
|
|
36
36
|
## メタ情報を表示する (`meta`)
|
|
37
37
|
|
|
38
|
-
`meta` は DDBJ Search の
|
|
38
|
+
`meta` は DDBJ Search の entry JSON を要約して表示します。
|
|
39
39
|
|
|
40
40
|
```sh
|
|
41
41
|
dratools meta DRR300000
|
|
@@ -67,7 +67,7 @@ dratools runs PRJNA341783
|
|
|
67
67
|
dratools runs PRJNA341783 | dratools get -O ~/Downloads
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Study や BioProject には多数の experiment や sample が含まれることがあります。`runs` はこれらを無制限には辿りません。上限を超えるとエラーで止まります。run へ直接リンクがある場合は、
|
|
70
|
+
Study や BioProject には多数の experiment や sample が含まれることがあります。`runs` はこれらを無制限には辿りません。上限を超えるとエラーで止まります。run へ直接リンクがある場合は、500 件を超えても制限の対象外です。レコードが大きい場合は、先に `tree` や `meta` で構造を確認してください。experiment や sample に絞ってから `runs` を使ってください。
|
|
71
71
|
|
|
72
72
|
## 合計サイズを確認する (`size`)
|
|
73
73
|
|
|
@@ -158,7 +158,7 @@ dratools url --json DRR000001
|
|
|
158
158
|
|
|
159
159
|
## 接続確認 (`probe`)
|
|
160
160
|
|
|
161
|
-
`probe`
|
|
161
|
+
`probe` は接続確認だけを行います。ファイルをダウンロードしません。
|
|
162
162
|
|
|
163
163
|
```sh
|
|
164
164
|
dratools probe --timeout 5 DRR000001
|
|
@@ -190,29 +190,30 @@ direct run を多数持つ親 accession では、`tree` は各 run を個別取
|
|
|
190
190
|
dratools get -O ~/Downloads DRR000001
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
ダウンロード中は `curl
|
|
193
|
+
ダウンロード中は `curl`, `wget`, `aria2c` のいずれかの進捗が標準エラーに出ます。取得したファイルは `Downloaded<TAB>PATH` と表示します。既存ファイルを再利用した場合は `Skipped<TAB>PATH` と表示します。これらは標準エラーに出ます。最後に `dratools get: N downloaded, M skipped` のサマリを出します。状態とパスは TAB 区切りです。パスだけを取り出すには `cut -f2` を使います。
|
|
194
194
|
|
|
195
|
-
###
|
|
195
|
+
### 既存ファイルの扱い
|
|
196
196
|
|
|
197
|
-
md5
|
|
197
|
+
DDBJ のメタデータに md5 が含まれることは多くありません。ほとんどの場合、md5 は得られません。以下では、まず md5 が無い場合の動作を示します。
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
同名のファイルが既にある場合、`get` はサーバにファイルサイズを問い合わせます。それをローカルのファイルサイズと比べます。
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
- サイズが同じなら、再取得しません。`Skipped` と表示します。
|
|
202
|
+
- ローカルのほうが小さいなら、再取得します。途中で中断したファイルが対象です。
|
|
203
|
+
- ローカルのほうが大きいなら、エラーになります。別物の可能性があるためです。`--force` で上書きできます。
|
|
204
204
|
|
|
205
|
-
|
|
205
|
+
md5 が得られる場合は、サイズではなく md5 で判定します。既存ファイルの md5 が一致すれば `Skipped` と表示します。ダウンロード後にも md5 を照合します。
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
```sh
|
|
210
|
-
dratools get --force -O ~/Downloads DRR000001
|
|
211
|
-
```
|
|
207
|
+
### オプション
|
|
212
208
|
|
|
213
|
-
|
|
209
|
+
| オプション | 動作 |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `--force` | 既存ファイルがあっても再取得します。 |
|
|
212
|
+
| `--skip-existing` | 同名のファイルがあれば、確認せずスキップします。サーバへの問い合わせを省きます。 |
|
|
213
|
+
| `--no-verify` | ダウンロード後の md5 照合を省きます。md5 が無い場合は照合しないので、効果はありません。 |
|
|
214
214
|
|
|
215
215
|
```sh
|
|
216
|
+
dratools get --force -O ~/Downloads DRR000001
|
|
216
217
|
dratools get --skip-existing -O ~/Downloads DRR000001
|
|
217
218
|
```
|
|
218
219
|
|
|
@@ -72,12 +72,39 @@ module Dratools
|
|
|
72
72
|
@client.fetch_resource_record(resource_type, accession)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
def direct_run_accessions_for(accession)
|
|
76
|
+
accession = accession.to_s.upcase
|
|
77
|
+
resource_type = resource_type_for(accession)
|
|
78
|
+
return [accession] if resource_type == DdbjRecordFields::SRA_RUN_RESOURCE_TYPE
|
|
79
|
+
|
|
80
|
+
@client.fetch_db_links(
|
|
81
|
+
resource_type,
|
|
82
|
+
accession,
|
|
83
|
+
target: DdbjRecordFields::SRA_RUN_RESOURCE_TYPE
|
|
84
|
+
).filter_map { |xref| xref_accession(xref) }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def direct_run_count_for(accession)
|
|
88
|
+
accession = accession.to_s.upcase
|
|
89
|
+
resource_type = resource_type_for(accession)
|
|
90
|
+
return 1 if resource_type == DdbjRecordFields::SRA_RUN_RESOURCE_TYPE
|
|
91
|
+
|
|
92
|
+
counts = @client.fetch_db_link_counts([{ type: resource_type, id: accession }])
|
|
93
|
+
counts.fetch([resource_type, accession], {}).fetch(DdbjRecordFields::SRA_RUN_RESOURCE_TYPE, 0)
|
|
94
|
+
end
|
|
95
|
+
|
|
75
96
|
def resource_type_for(accession)
|
|
76
97
|
@resource_type_classifier.resource_type_for(accession)
|
|
77
98
|
end
|
|
78
99
|
|
|
79
100
|
private
|
|
80
101
|
|
|
102
|
+
def xref_accession(xref)
|
|
103
|
+
xref[DdbjRecordFields::IDENTIFIER_KEY] ||
|
|
104
|
+
xref[DdbjRecordFields::ID_KEY] ||
|
|
105
|
+
xref[DdbjRecordFields::ACCESSION_KEY]
|
|
106
|
+
end
|
|
107
|
+
|
|
81
108
|
def attach_downloads(node, file_type:)
|
|
82
109
|
if node.run? && node.record
|
|
83
110
|
downloads = @download_candidate_builder.build_from_run_record(node.record)
|
|
@@ -4,14 +4,14 @@ require_relative 'ddbj_record_fields'
|
|
|
4
4
|
require_relative 'errors'
|
|
5
5
|
|
|
6
6
|
module Dratools
|
|
7
|
-
# accession の接頭辞から DDBJ
|
|
7
|
+
# accession の接頭辞から DDBJ Search entry type を判定する。
|
|
8
8
|
class AccessionResourceTypeClassifier
|
|
9
9
|
RUN_PREFIXES = /\A[DES]RR\d+\z/
|
|
10
10
|
EXPERIMENT_PREFIXES = /\A[DES]RX\d+\z/
|
|
11
11
|
SAMPLE_PREFIXES = /\A[DES]RS\d+\z/
|
|
12
12
|
STUDY_PREFIXES = /\A[DES]RP\d+\z/
|
|
13
13
|
SUBMISSION_PREFIXES = /\A[DES]RA\d+\z/
|
|
14
|
-
BIOPROJECT_PREFIXES = /\APRJ
|
|
14
|
+
BIOPROJECT_PREFIXES = /\APRJ[DEN][A-Z]\d+\z/
|
|
15
15
|
BIOSAMPLE_PREFIXES = /\ASAM(?:D|N|EA|EG)?\d+\z/
|
|
16
16
|
|
|
17
17
|
TYPE_BY_ACCESSION = [
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'version'
|
|
4
4
|
require_relative 'accession_resolver'
|
|
5
|
+
require_relative 'ddbj_resource_client'
|
|
5
6
|
require_relative 'download_service'
|
|
7
|
+
require_relative 'progress_reporter'
|
|
6
8
|
require_relative 'commands/url_command'
|
|
7
9
|
require_relative 'commands/get_command'
|
|
8
10
|
require_relative 'commands/probe_command'
|
|
@@ -67,18 +69,22 @@ module Dratools
|
|
|
67
69
|
|
|
68
70
|
def initialize(
|
|
69
71
|
argv,
|
|
70
|
-
resolver:
|
|
72
|
+
resolver: nil,
|
|
71
73
|
downloader: DownloadService.new,
|
|
72
74
|
stdout: $stdout,
|
|
73
75
|
stderr: $stderr,
|
|
74
76
|
stdin: $stdin
|
|
75
77
|
)
|
|
76
78
|
@argv = argv
|
|
77
|
-
@resolver = resolver
|
|
78
|
-
@downloader = downloader
|
|
79
79
|
@stdout = stdout
|
|
80
80
|
@stderr = stderr
|
|
81
81
|
@stdin = stdin
|
|
82
|
+
@progress = ProgressReporter.new(
|
|
83
|
+
io: stderr,
|
|
84
|
+
enabled: interactive?(stderr)
|
|
85
|
+
)
|
|
86
|
+
@resolver = resolver || default_resolver
|
|
87
|
+
@downloader = downloader
|
|
82
88
|
end
|
|
83
89
|
|
|
84
90
|
def run
|
|
@@ -108,14 +114,24 @@ module Dratools
|
|
|
108
114
|
@argv.drop(1),
|
|
109
115
|
resolver: @resolver,
|
|
110
116
|
downloader: @downloader,
|
|
111
|
-
stdout: @stdout,
|
|
112
|
-
stderr: @
|
|
117
|
+
stdout: @progress.clearing_io(@stdout),
|
|
118
|
+
stderr: @progress.clearing_io,
|
|
113
119
|
stdin: @stdin
|
|
114
120
|
).run
|
|
121
|
+
ensure
|
|
122
|
+
@progress.finish
|
|
115
123
|
end
|
|
116
124
|
|
|
117
125
|
private
|
|
118
126
|
|
|
127
|
+
def default_resolver
|
|
128
|
+
AccessionResolver.new(client: DdbjResourceClient.new(progress: @progress))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def interactive?(io)
|
|
132
|
+
io.respond_to?(:tty?) && io.tty?
|
|
133
|
+
end
|
|
134
|
+
|
|
119
135
|
def print_help(stream)
|
|
120
136
|
stream.puts "Usage: #{COMMAND_NAME} <command> [options] [ACCESSION ...]"
|
|
121
137
|
stream.puts ''
|
|
@@ -124,12 +140,7 @@ module Dratools
|
|
|
124
140
|
stream.puts format(' %-7<name>s %<summary>s', name: name, summary: summary)
|
|
125
141
|
end
|
|
126
142
|
stream.puts ''
|
|
127
|
-
stream.puts '
|
|
128
|
-
SUBCOMMAND_ALIASES.each do |alias_name, canonical|
|
|
129
|
-
stream.puts format(' %-7<a>s -> %<c>s', a: alias_name, c: canonical)
|
|
130
|
-
end
|
|
131
|
-
stream.puts ''
|
|
132
|
-
stream.puts "Run '#{COMMAND_NAME} <command> --help' for command options."
|
|
143
|
+
stream.puts "各コマンドのオプションは '#{COMMAND_NAME} <command> --help' で確認できます。"
|
|
133
144
|
stream.puts ''
|
|
134
145
|
stream.puts 'Examples:'
|
|
135
146
|
USAGE_EXAMPLES.each { |example| stream.puts " #{example}" }
|
|
@@ -6,7 +6,7 @@ require_relative 'base_command'
|
|
|
6
6
|
|
|
7
7
|
module Dratools
|
|
8
8
|
module Commands
|
|
9
|
-
# DDBJ
|
|
9
|
+
# DDBJ Search entry JSON のメタ情報を要約表示する。
|
|
10
10
|
class MetaCommand < BaseCommand
|
|
11
11
|
LABEL_WIDTH = 18
|
|
12
12
|
|
|
@@ -21,7 +21,7 @@ module Dratools
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def configure_parser(parser)
|
|
24
|
-
parser.on('--json', '生の
|
|
24
|
+
parser.on('--json', '生の entry JSON を整形して表示する') { @options[:json] = true }
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def usage_examples
|
|
@@ -6,8 +6,6 @@ module Dratools
|
|
|
6
6
|
module Commands
|
|
7
7
|
# accession を run accession のフラットな一覧に展開する。
|
|
8
8
|
class RunsCommand < BaseCommand
|
|
9
|
-
XREF_URL_PATTERN = %r{/(?:resource|search/entry)/sra-run/([^/?#.]+)}
|
|
10
|
-
|
|
11
9
|
private
|
|
12
10
|
|
|
13
11
|
def command_name
|
|
@@ -41,29 +39,7 @@ module Dratools
|
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
def direct_run_accessions_for(accession)
|
|
44
|
-
|
|
45
|
-
if record[DdbjRecordFields::TYPE_KEY] == DdbjRecordFields::SRA_RUN_RESOURCE_TYPE
|
|
46
|
-
return [record_accession(record)].compact
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
record.fetch(DdbjRecordFields::DB_XREFS_KEY, []).filter_map do |xref|
|
|
50
|
-
next unless xref[DdbjRecordFields::TYPE_KEY] == DdbjRecordFields::SRA_RUN_RESOURCE_TYPE
|
|
51
|
-
|
|
52
|
-
xref[DdbjRecordFields::IDENTIFIER_KEY] ||
|
|
53
|
-
xref[DdbjRecordFields::ID_KEY] ||
|
|
54
|
-
run_accession_from_url(xref[DdbjRecordFields::URL_KEY])
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def record_accession(record)
|
|
59
|
-
record[DdbjRecordFields::ACCESSION_KEY] ||
|
|
60
|
-
record[DdbjRecordFields::IDENTIFIER_KEY] ||
|
|
61
|
-
record[DdbjRecordFields::ID_KEY] ||
|
|
62
|
-
record[DdbjRecordFields::PRIMARY_ID_KEY]
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def run_accession_from_url(url)
|
|
66
|
-
url.to_s.match(XREF_URL_PATTERN)&.[](1)
|
|
42
|
+
@resolver.direct_run_accessions_for(accession)
|
|
67
43
|
end
|
|
68
44
|
end
|
|
69
45
|
end
|
|
@@ -140,23 +140,25 @@ module Dratools
|
|
|
140
140
|
|
|
141
141
|
def fetch_record_for_size(accession)
|
|
142
142
|
max_direct_runs = Config.size_max_direct_runs
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
ddbj_record
|
|
143
|
+
validate_direct_run_expansion_count!(accession, max_direct_runs)
|
|
144
|
+
@resolver.fetch_record_for(accession)
|
|
146
145
|
end
|
|
147
146
|
|
|
148
|
-
def
|
|
147
|
+
def validate_direct_run_expansion_count!(accession, max_direct_runs)
|
|
149
148
|
return unless max_direct_runs
|
|
150
149
|
|
|
151
|
-
direct_run_count =
|
|
152
|
-
xref[DdbjRecordFields::TYPE_KEY] == DdbjRecordFields::SRA_RUN_RESOURCE_TYPE
|
|
153
|
-
end
|
|
150
|
+
direct_run_count = @resolver.direct_run_count_for(accession)
|
|
154
151
|
return if direct_run_count <= max_direct_runs
|
|
155
152
|
|
|
153
|
+
raise_direct_run_limit_error(accession, direct_run_count, max_direct_runs)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def raise_direct_run_limit_error(accession, direct_run_count, max_direct_runs)
|
|
156
157
|
raise InvalidRecordError,
|
|
157
158
|
"#{accession.to_s.upcase} has #{direct_run_count} direct runs; " \
|
|
158
159
|
"size expands at most #{max_direct_runs} direct runs from one parent accession. " \
|
|
159
|
-
"Use `#{Dratools::NAME} runs #{accession}` and pass narrower accessions
|
|
160
|
+
"Use `#{Dratools::NAME} runs #{accession}` and pass narrower accessions, " \
|
|
161
|
+
"or set #{Config::SIZE_MAX_DIRECT_RUNS_ENV}=unlimited."
|
|
160
162
|
end
|
|
161
163
|
end
|
|
162
164
|
end
|
|
@@ -73,19 +73,20 @@ module Dratools
|
|
|
73
73
|
|
|
74
74
|
def fetch_record_for_url(accession)
|
|
75
75
|
max_direct_runs = Config.url_max_direct_runs
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ddbj_record
|
|
76
|
+
validate_direct_run_expansion_count!(accession, max_direct_runs)
|
|
77
|
+
@resolver.fetch_record_for(accession)
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
def
|
|
80
|
+
def validate_direct_run_expansion_count!(accession, max_direct_runs)
|
|
82
81
|
return unless max_direct_runs
|
|
83
82
|
|
|
84
|
-
direct_run_count =
|
|
85
|
-
xref[DdbjRecordFields::TYPE_KEY] == DdbjRecordFields::SRA_RUN_RESOURCE_TYPE
|
|
86
|
-
end
|
|
83
|
+
direct_run_count = @resolver.direct_run_count_for(accession)
|
|
87
84
|
return if direct_run_count <= max_direct_runs
|
|
88
85
|
|
|
86
|
+
raise_direct_run_limit_error(accession, direct_run_count, max_direct_runs)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def raise_direct_run_limit_error(accession, direct_run_count, max_direct_runs)
|
|
89
90
|
raise InvalidRecordError,
|
|
90
91
|
"#{accession.to_s.upcase} has #{direct_run_count} direct runs; " \
|
|
91
92
|
"url expands at most #{max_direct_runs} direct runs from one parent accession. " \
|
data/lib/dratools/config.rb
CHANGED
|
@@ -14,16 +14,18 @@ module Dratools
|
|
|
14
14
|
DOWNLOAD_STALL_SPEED_ENV = 'DRATOOLS_DOWNLOAD_STALL_SPEED'
|
|
15
15
|
DOWNLOAD_RETRY_COUNT_ENV = 'DRATOOLS_DOWNLOAD_RETRY_COUNT'
|
|
16
16
|
DOWNLOAD_RETRY_WAIT_ENV = 'DRATOOLS_DOWNLOAD_RETRY_WAIT'
|
|
17
|
+
DOWNLOAD_COMMAND_ENV = 'DRATOOLS_DOWNLOAD_COMMAND'
|
|
17
18
|
|
|
18
|
-
DEFAULT_MAX_RECURSIVE_NON_RUN_XREFS =
|
|
19
|
-
DEFAULT_TREE_MAX_DIRECT_RUNS =
|
|
20
|
-
DEFAULT_URL_MAX_DIRECT_RUNS =
|
|
21
|
-
DEFAULT_SIZE_MAX_DIRECT_RUNS =
|
|
19
|
+
DEFAULT_MAX_RECURSIVE_NON_RUN_XREFS = 500
|
|
20
|
+
DEFAULT_TREE_MAX_DIRECT_RUNS = 200
|
|
21
|
+
DEFAULT_URL_MAX_DIRECT_RUNS = 200
|
|
22
|
+
DEFAULT_SIZE_MAX_DIRECT_RUNS = 200
|
|
22
23
|
DEFAULT_DOWNLOAD_CONNECT_TIMEOUT_SECONDS = 30
|
|
23
24
|
DEFAULT_DOWNLOAD_STALL_TIMEOUT_SECONDS = 60
|
|
24
25
|
DEFAULT_DOWNLOAD_STALL_SPEED_BYTES_PER_SECOND = 1024
|
|
25
26
|
DEFAULT_DOWNLOAD_RETRY_COUNT = 3
|
|
26
27
|
DEFAULT_DOWNLOAD_RETRY_WAIT_SECONDS = 5
|
|
28
|
+
SUPPORTED_DOWNLOAD_COMMANDS = %w[curl wget aria2c].freeze
|
|
27
29
|
UNLIMITED_VALUE = 'unlimited'
|
|
28
30
|
|
|
29
31
|
module_function
|
|
@@ -70,6 +72,18 @@ module Dratools
|
|
|
70
72
|
positive_integer(DOWNLOAD_RETRY_WAIT_ENV, DEFAULT_DOWNLOAD_RETRY_WAIT_SECONDS)
|
|
71
73
|
end
|
|
72
74
|
|
|
75
|
+
def download_command
|
|
76
|
+
value = ENV.fetch(DOWNLOAD_COMMAND_ENV, '').strip
|
|
77
|
+
return nil if value.empty?
|
|
78
|
+
return value if SUPPORTED_DOWNLOAD_COMMANDS.include?(value)
|
|
79
|
+
|
|
80
|
+
invalid_environment_value!(
|
|
81
|
+
DOWNLOAD_COMMAND_ENV,
|
|
82
|
+
value,
|
|
83
|
+
SUPPORTED_DOWNLOAD_COMMANDS.join(' or ')
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
73
87
|
def positive_integer_or_unlimited(name, default)
|
|
74
88
|
value = ENV.fetch(name, '').strip
|
|
75
89
|
return default if value.empty?
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Dratools
|
|
4
|
-
# DDBJ Search
|
|
4
|
+
# DDBJ Search entry JSON で使う resource type とキー名をまとめる。
|
|
5
5
|
module DdbjRecordFields
|
|
6
6
|
SRA_RUN_RESOURCE_TYPE = 'sra-run'
|
|
7
7
|
SRA_EXPERIMENT_RESOURCE_TYPE = 'sra-experiment'
|
|
@@ -18,18 +18,13 @@ module Dratools
|
|
|
18
18
|
DB_XREFS_KEY = 'dbXrefs'
|
|
19
19
|
CHILD_BIOPROJECTS_KEY = 'childBioProjects'
|
|
20
20
|
TYPE_KEY = 'type'
|
|
21
|
-
URL_KEY = 'url'
|
|
22
|
-
FTP_URL_KEY = 'ftpUrl'
|
|
23
21
|
ID_KEY = 'id'
|
|
24
22
|
IDENTIFIER_KEY = 'identifier'
|
|
25
23
|
ACCESSION_KEY = 'accession'
|
|
26
24
|
PRIMARY_ID_KEY = 'primaryId'
|
|
27
|
-
DOWNLOAD_URL_KEY = 'downloadUrl'
|
|
28
25
|
DISTRIBUTION_KEY = 'distribution'
|
|
29
26
|
CONTENT_URL_KEY = 'contentUrl'
|
|
30
27
|
CONTENT_SIZE_KEY = 'contentSize'
|
|
31
|
-
SIZE_KEY = 'size'
|
|
32
|
-
FILE_SIZE_KEY = 'fileSize'
|
|
33
28
|
MD5_KEY = 'md5'
|
|
34
29
|
MD5_SUM_KEY = 'md5sum'
|
|
35
30
|
ENCODING_FORMAT_KEY = 'encodingFormat'
|