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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48f8f5324602da01372e8590c74a22ae9a3474b125c96c3d2bf8de501e80160a
4
- data.tar.gz: 9980db4882cefe19b2143496262ec785a6994e04d3c75ac6edc23deb0607f226
3
+ metadata.gz: 580e0395df351cc56d957e756ad8368b663d9902f4722983e268093e97ec0809
4
+ data.tar.gz: 39206bc915245cea263f476cf7e5397f7e1027c1c21de3500e0c0c573f1c2c41
5
5
  SHA512:
6
- metadata.gz: 687ca0459f6bead0d1ca7d4349fcb023b1c142b05e4b2e4e1eda461b54a42209a3ac7cec08d8552522921c691da38ffe89d367270859dd15cefc17af9bf99210
7
- data.tar.gz: e2ea8839bcb20737af0880e20694d266c0fc1ee9ab0a2f77e6b027fbe4750b09bba2e9f42ae839a4492d94dbb6f22ed4118ff3ffb72ebe206bd2689ce9e6d795
6
+ metadata.gz: 91bfee6e903c4334818f4dce7da9a30df66db16412cefcfa91ed384e5e0f031acedb69d1f17a6765fd86602072219c5e10a80043ced268d9b966779673c3fef0
7
+ data.tar.gz: 000f9ca102c3deacd8ae0b1c1d9f8edea16b596f65383c3f0fb925f413ca773390e0d1a7e9615f728b6f8b9d41afe7d5b8cdc0be4814cb319af34ce61fea1dd5
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # dratools
2
2
 
3
3
  [![CI](https://github.com/kojix2/dratools/actions/workflows/ci.yml/badge.svg)](https://github.com/kojix2/dratools/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/dratools.svg)](https://badge.fury.io/rb/dratools)
4
5
  [![Lines of Code](https://img.shields.io/endpoint?url=https%3A%2F%2Ftokei.kojix2.net%2Fbadge%2Fgithub%2Fkojix2%2Fdratools%2Flines)](https://tokei.kojix2.net/github/kojix2/dratools)
6
+ [![DOI](https://zenodo.org/badge/1281844096.svg)](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 # resource JSON を表示する
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
- 実際のファイル転送には `curl` または `wget` を使います。ダウンロード中は進捗表示を端末にそのまま流します。md5 が得られる場合だけ、ダウンロード後に照合します。
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
- なるべく手間をかけずに、寝ている間にデータをダウンロードしたい方は「NASに課金する」という手段もあります。
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 resource JSON
15
+ -> DDBJ Search entry JSON (/search/api/entries/{type}/{id}.json)
16
16
  -> sra-run record
17
- -> downloadUrl
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` は resource JSON のサイズ情報を使いません。実レコードにはサイズや md5 が含まれないことが多いためです。代わりに、解決した URL に HTTP `HEAD` を送ります。`Content-Length` を合計します。
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` は使いません。`curl` / `wget` の進捗を端末にそのまま表示するためです。失敗した場合は、コマンド行と終了ステータスを `CommandError` にします。
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 -` または `wget --continue` でレジュームを試みる
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
- 実ファイルの転送だけは外部コマンドに任せます。環境にある `curl` または `wget` を使います。
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` | `100` | `runs` などが direct run を持たない親レコードから experiment/sample/study などの非 run レコードを再帰的に辿る最大件数 |
10
- | `DRATOOLS_TREE_MAX_DIRECT_RUNS` | `50` | `tree` が direct run レコードを個別取得して URL まで展開する最大 run 件数。超えた場合は件数だけを要約表示 |
11
- | `DRATOOLS_URL_MAX_DIRECT_RUNS` | `50` | `url` が 1 つの親 accession から direct run を暗黙展開して URL を解決する最大 run 件数 |
12
- | `DRATOOLS_SIZE_MAX_DIRECT_RUNS` | `50` | `size` が 1 つの親 accession から direct run を暗黙展開して HEAD する最大 run 件数 |
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` | 失速判定に使う最低転送速度。単位は bytes/sec |
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` 200 件まで URL を展開する:
35
+ `tree` direct run 展開の既定値は 200 件です。
36
+ 500 件に上げる例:
31
37
 
32
38
  ```sh
33
- DRATOOLS_TREE_MAX_DIRECT_RUNS=200 dratools tree PRJDB12740
39
+ DRATOOLS_TREE_MAX_DIRECT_RUNS=500 dratools tree PRJDB12740
34
40
  ```
35
41
 
36
42
  この値を小さくすると、展開しない direct run は要約だけを表示します。
37
43
 
38
- `size` 100 件まで direct run を暗黙展開する:
44
+ `size` direct run 暗黙展開の既定値は 200 件です。
45
+ 500 件に上げる例:
39
46
 
40
47
  ```sh
41
- DRATOOLS_SIZE_MAX_DIRECT_RUNS=100 dratools size PRJDB12740
48
+ DRATOOLS_SIZE_MAX_DIRECT_RUNS=500 dratools size PRJDB12740
42
49
  ```
43
50
 
44
- `url` 100 件まで direct run を暗黙展開する:
51
+ `url` direct run 暗黙展開の既定値は 200 件です。
52
+ 500 件に上げる例:
45
53
 
46
54
  ```sh
47
- DRATOOLS_URL_MAX_DIRECT_RUNS=100 dratools url --tsv PRJDB12740
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 の総数は制限しません。まず `meta` `tree` で構造を確認してください。必要なら `runs` で accession を絞ってください。その後で重い操作を実行してください。
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 の resource JSON を要約して表示します。
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 へ直接リンクがある場合は、100 件を超えても制限の対象外です。レコードが大きい場合は、先に `tree` や `meta` で構造を確認してください。experiment や sample に絞ってから `runs` を使ってください。
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` または `wget` の進捗が標準エラーに出ます。取得したファイルは `Downloaded<TAB>PATH` と表示します。既存ファイルを再利用した場合は `Skipped<TAB>PATH` と表示します。これらは標準エラーに出ます。最後に `dratools get: N downloaded, M skipped` のサマリを出します。状態とパスは TAB 区切りです。パスだけを取り出すには `cut -f2` を使います。
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 が得られる場合、`get` はダウンロード後に照合します。md5 が無い場合、既定では既存ファイルをスキップしません。
197
+ DDBJ のメタデータに md5 が含まれることは多くありません。ほとんどの場合、md5 は得られません。以下では、まず md5 が無い場合の動作を示します。
198
198
 
199
- 検証を省略する場合は `--no-verify` を付けます。
199
+ 同名のファイルが既にある場合、`get` はサーバにファイルサイズを問い合わせます。それをローカルのファイルサイズと比べます。
200
200
 
201
- ```sh
202
- dratools get --no-verify -O ~/Downloads DRR000001
203
- ```
201
+ - サイズが同じなら、再取得しません。`Skipped` と表示します。
202
+ - ローカルのほうが小さいなら、再取得します。途中で中断したファイルが対象です。
203
+ - ローカルのほうが大きいなら、エラーになります。別物の可能性があるためです。`--force` で上書きできます。
204
204
 
205
- 同名のファイルが既にある場合を考えます。その md5 DDBJ の値と一致すれば、再ダウンロードしません。`Skipped` と表示します。
205
+ md5 が得られる場合は、サイズではなく md5 で判定します。既存ファイルの md5 が一致すれば `Skipped` と表示します。ダウンロード後にも md5 を照合します。
206
206
 
207
- 既存ファイルがあっても必ず再取得する場合は `--force` を付けます。
208
-
209
- ```sh
210
- dratools get --force -O ~/Downloads DRR000001
211
- ```
207
+ ### オプション
212
208
 
213
- md5 を確認せずに既存ファイルを再取得しない場合は `--skip-existing` を付けます。
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 resource API type を判定する。
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(?:DA|DB|NA|EB)\d+\z/
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: AccessionResolver.new,
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: @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 'Aliases:'
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 resource JSON のメタ情報を要約表示する。
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', '生の resource JSON を整形して表示する') { @options[:json] = true }
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
- record = @resolver.fetch_record_for(accession)
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
- ddbj_record = @resolver.fetch_record_for(accession)
144
- validate_direct_run_expansion_size!(accession, ddbj_record, max_direct_runs)
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 validate_direct_run_expansion_size!(accession, ddbj_record, max_direct_runs)
147
+ def validate_direct_run_expansion_count!(accession, max_direct_runs)
149
148
  return unless max_direct_runs
150
149
 
151
- direct_run_count = ddbj_record.fetch(DdbjRecordFields::DB_XREFS_KEY, []).count do |xref|
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
- ddbj_record = @resolver.fetch_record_for(accession)
77
- validate_direct_run_expansion_size!(accession, ddbj_record, max_direct_runs)
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 validate_direct_run_expansion_size!(accession, ddbj_record, max_direct_runs)
80
+ def validate_direct_run_expansion_count!(accession, max_direct_runs)
82
81
  return unless max_direct_runs
83
82
 
84
- direct_run_count = ddbj_record.fetch(DdbjRecordFields::DB_XREFS_KEY, []).count do |xref|
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. " \
@@ -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 = 100
19
- DEFAULT_TREE_MAX_DIRECT_RUNS = 50
20
- DEFAULT_URL_MAX_DIRECT_RUNS = 50
21
- DEFAULT_SIZE_MAX_DIRECT_RUNS = 50
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 resource JSON で使う resource type とキー名をまとめる。
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'