geoseeq 0.6.14.dev7__py3-none-any.whl → 0.7.0__py3-none-any.whl
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.
- geoseeq/cli/download.py +2 -2
- geoseeq/cli/main.py +1 -1
- geoseeq/cli/upload/__init__.py +8 -3
- geoseeq/cli/upload/upload.py +114 -0
- geoseeq/cli/upload/upload_advanced.py +230 -0
- geoseeq/cli/upload/upload_reads.py +13 -4
- geoseeq/constants.py +4 -1
- geoseeq/id_constructors/from_ids.py +20 -14
- geoseeq/id_constructors/utils.py +17 -0
- geoseeq/result/result_file.py +2 -1
- geoseeq/result/result_folder.py +8 -0
- geoseeq/result/smart_objects.py +40 -0
- geoseeq/smart_table.py +3 -2
- geoseeq/smart_tree.py +57 -0
- geoseeq/upload_download_manager.py +16 -6
- {geoseeq-0.6.14.dev7.dist-info → geoseeq-0.7.0.dist-info}/METADATA +3 -2
- {geoseeq-0.6.14.dev7.dist-info → geoseeq-0.7.0.dist-info}/RECORD +21 -19
- {geoseeq-0.6.14.dev7.dist-info → geoseeq-0.7.0.dist-info}/WHEEL +1 -1
- {geoseeq-0.6.14.dev7.dist-info → geoseeq-0.7.0.dist-info}/entry_points.txt +0 -0
- {geoseeq-0.6.14.dev7.dist-info → geoseeq-0.7.0.dist-info/licenses}/LICENSE +0 -0
- {geoseeq-0.6.14.dev7.dist-info → geoseeq-0.7.0.dist-info}/top_level.txt +0 -0
geoseeq/cli/download.py
CHANGED
@@ -414,7 +414,7 @@ def _make_read_configs(download_results, config_dir="."):
|
|
414
414
|
"reads_1": ["small.fq.gz"],
|
415
415
|
"reads_2": [],
|
416
416
|
"fastq_checksum": "",
|
417
|
-
"data_type": "
|
417
|
+
"data_type": "single",
|
418
418
|
"bdx_result_dir": "results",
|
419
419
|
"geoseeq_uuid": "05bf22e9-9d25-42db-af25-31bc538a7006"
|
420
420
|
}
|
@@ -428,7 +428,7 @@ def _make_read_configs(download_results, config_dir="."):
|
|
428
428
|
"reads_1": [],
|
429
429
|
"reads_2": [],
|
430
430
|
"fastq_checksum": "",
|
431
|
-
"data_type":
|
431
|
+
"data_type": read_type,
|
432
432
|
"bdx_result_dir": "results",
|
433
433
|
"geoseeq_uuid": sample.uuid,
|
434
434
|
}
|
geoseeq/cli/main.py
CHANGED
@@ -55,7 +55,7 @@ def version():
|
|
55
55
|
Use of this tool implies acceptance of the GeoSeeq End User License Agreement.
|
56
56
|
Run `geoseeq eula show` to view the EULA.
|
57
57
|
"""
|
58
|
-
click.echo('0.
|
58
|
+
click.echo('0.7.0') # remember to update pyproject.toml
|
59
59
|
|
60
60
|
|
61
61
|
@main.group('advanced')
|
geoseeq/cli/upload/__init__.py
CHANGED
@@ -4,9 +4,11 @@ from .upload import (
|
|
4
4
|
cli_upload_file,
|
5
5
|
cli_upload_folder,
|
6
6
|
cli_metadata,
|
7
|
+
cli_upload_smart_table,
|
8
|
+
cli_upload_smart_tree,
|
7
9
|
)
|
8
10
|
from .upload_reads import cli_upload_reads_wizard
|
9
|
-
from .upload_advanced import cli_find_urls_for_reads
|
11
|
+
from .upload_advanced import cli_find_urls_for_reads, cli_upload_from_config
|
10
12
|
|
11
13
|
@click.group('upload')
|
12
14
|
def cli_upload():
|
@@ -17,10 +19,13 @@ cli_upload.add_command(cli_upload_reads_wizard)
|
|
17
19
|
cli_upload.add_command(cli_upload_file)
|
18
20
|
cli_upload.add_command(cli_upload_folder)
|
19
21
|
cli_upload.add_command(cli_metadata)
|
22
|
+
cli_upload.add_command(cli_upload_smart_table)
|
23
|
+
cli_upload.add_command(cli_upload_smart_tree)
|
20
24
|
|
21
|
-
@
|
25
|
+
@cli_upload.group('advanced')
|
22
26
|
def cli_upload_advanced():
|
23
27
|
"""Advanced tools to upload files to GeoSeeq."""
|
24
28
|
pass
|
25
29
|
|
26
|
-
cli_upload_advanced.add_command(cli_find_urls_for_reads)
|
30
|
+
cli_upload_advanced.add_command(cli_find_urls_for_reads)
|
31
|
+
cli_upload_advanced.add_command(cli_upload_from_config)
|
geoseeq/cli/upload/upload.py
CHANGED
@@ -263,3 +263,117 @@ def cli_metadata(state, overwrite, yes, private, create, index_col, encoding, pr
|
|
263
263
|
sample.metadata = new_meta
|
264
264
|
sample.idem()
|
265
265
|
click.echo(f'Wrote metadata for {len(samples)} samples')
|
266
|
+
|
267
|
+
|
268
|
+
@click.command('smart-table')
|
269
|
+
@use_common_state
|
270
|
+
@overwrite_option
|
271
|
+
@yes_option
|
272
|
+
@private_option
|
273
|
+
@click.option('-n', '--geoseeq-file-name', default=None,
|
274
|
+
help='Specify a different name for the file on GeoSeeq than the local file name.',
|
275
|
+
show_default=True)
|
276
|
+
@folder_id_arg
|
277
|
+
@click.argument('file_path', type=click.Path(exists=True), nargs=1)
|
278
|
+
def cli_upload_smart_table(state, overwrite, yes, private, folder_id, geoseeq_file_name, file_path):
|
279
|
+
"""Upload a smart table to GeoSeeq.
|
280
|
+
|
281
|
+
This command uploads a smart table to a project or sample on GeoSeeq. It can be used to upload
|
282
|
+
a single file to a folder at once.
|
283
|
+
|
284
|
+
---
|
285
|
+
|
286
|
+
Example Usage:
|
287
|
+
|
288
|
+
\b
|
289
|
+
# Upload a smart table from a file
|
290
|
+
$ geoseeq upload smart-table "My Org/My Project/My Sample/My Folder" /path/to/my_table.csv
|
291
|
+
|
292
|
+
\b
|
293
|
+
# Upload a smart table from a file but name it "My Smart Table" on GeoSeeq
|
294
|
+
$ geoseeq upload smart-table "My Org/My Project/My Sample/My Folder" /path/to/my_table.csv -n "My Smart Table"
|
295
|
+
|
296
|
+
---
|
297
|
+
|
298
|
+
Command Arguments:
|
299
|
+
|
300
|
+
[FOLDER_ID] Can be a folder UUID, GeoSeeq Resource Number (GRN), or an
|
301
|
+
names for an org, project, sample, folder separated by a slash. Can exclude
|
302
|
+
the sample name if the folder is for a project.
|
303
|
+
|
304
|
+
[FILE_PATH] A path to a file on your local machine.
|
305
|
+
|
306
|
+
---
|
307
|
+
"""
|
308
|
+
knex = state.get_knex()
|
309
|
+
result_folder = handle_folder_id(knex, folder_id, yes=yes, private=private)
|
310
|
+
|
311
|
+
if not geoseeq_file_name:
|
312
|
+
geoseeq_file_name = basename(file_path)
|
313
|
+
|
314
|
+
if not overwrite and result_folder.result_file(geoseeq_file_name).exists():
|
315
|
+
raise click.UsageError(f'{geoseeq_file_name} already exists in {result_folder}. Use --overwrite to overwrite it.')
|
316
|
+
|
317
|
+
result_file = result_folder.result_file(geoseeq_file_name)
|
318
|
+
smart_table = result_file.as_smart_table()
|
319
|
+
smart_table.import_csv(file_path)
|
320
|
+
|
321
|
+
|
322
|
+
@click.command('smart-tree')
|
323
|
+
@use_common_state
|
324
|
+
@click.option('-m/-nm', '--make-name-map/--no-name-map', default=True, help="Create a sample name map with all samples currently in the project.")
|
325
|
+
@overwrite_option
|
326
|
+
@yes_option
|
327
|
+
@private_option
|
328
|
+
@click.option('-n', '--geoseeq-file-name', default=None,
|
329
|
+
help='Specify a different name for the file on GeoSeeq than the local file name.',
|
330
|
+
show_default=True)
|
331
|
+
@folder_id_arg
|
332
|
+
@click.argument('newick_file_path', type=click.Path(exists=True), nargs=1)
|
333
|
+
def cli_upload_smart_tree(state, make_name_map, overwrite, yes, private, folder_id, geoseeq_file_name, newick_file_path):
|
334
|
+
"""Upload a smart tree to GeoSeeq.
|
335
|
+
|
336
|
+
This command uploads a smart tree to a project or sample on GeoSeeq. It can be used to upload
|
337
|
+
a single file to a folder at once.
|
338
|
+
|
339
|
+
---
|
340
|
+
|
341
|
+
Example Usage:
|
342
|
+
|
343
|
+
\b
|
344
|
+
# Upload a smart tree from a file
|
345
|
+
$ geoseeq upload smart-tree "My Org/My Project/My Sample/My Folder" /path/to/my_tree.nwk
|
346
|
+
|
347
|
+
\b
|
348
|
+
# Upload a smart tree from a file but name it "My Smart Tree" on GeoSeeq
|
349
|
+
$ geoseeq upload smart-tree "My Org/My Project/My Sample/My Folder" /path/to/my_tree.nwk -n "My Smart Tree"
|
350
|
+
|
351
|
+
---
|
352
|
+
|
353
|
+
Command Arguments:
|
354
|
+
|
355
|
+
[FOLDER_ID] Can be a folder UUID, GeoSeeq Resource Number (GRN), or an
|
356
|
+
names for an org, project, sample, folder separated by a slash. Can exclude
|
357
|
+
the sample name if the folder is for a project.
|
358
|
+
|
359
|
+
[NEWICK_FILE_PATH] A path to a newick file on your local machine.
|
360
|
+
|
361
|
+
---
|
362
|
+
"""
|
363
|
+
knex = state.get_knex()
|
364
|
+
result_folder = handle_folder_id(knex, folder_id, yes=yes, private=private)
|
365
|
+
|
366
|
+
if not geoseeq_file_name:
|
367
|
+
geoseeq_file_name = basename(newick_file_path)
|
368
|
+
|
369
|
+
if not overwrite and result_folder.result_file(geoseeq_file_name).exists():
|
370
|
+
raise click.UsageError(f'{geoseeq_file_name} already exists in {result_folder}. Use --overwrite to overwrite it.')
|
371
|
+
|
372
|
+
result_file = result_folder.result_file(geoseeq_file_name)
|
373
|
+
smart_tree = result_file.as_smart_tree()
|
374
|
+
with open(newick_file_path) as f:
|
375
|
+
newick_str = f.read()
|
376
|
+
smart_tree.create_from_newick(newick_str)
|
377
|
+
if make_name_map:
|
378
|
+
smart_tree.add_all_samples_to_map(result_folder.project)
|
379
|
+
smart_tree.idem()
|
@@ -24,6 +24,16 @@ from geoseeq.cli.shared_params import (
|
|
24
24
|
|
25
25
|
from geoseeq.constants import FASTQ_MODULE_NAMES
|
26
26
|
from geoseeq.cli.progress_bar import PBarManager
|
27
|
+
import pandas as pd
|
28
|
+
from typing import Dict, Optional
|
29
|
+
from geoseeq.id_constructors.from_ids import (
|
30
|
+
org_from_id,
|
31
|
+
project_from_id,
|
32
|
+
sample_from_id,
|
33
|
+
result_folder_from_id,
|
34
|
+
result_file_from_id,
|
35
|
+
)
|
36
|
+
from geoseeq.upload_download_manager import GeoSeeqUploadManager
|
27
37
|
|
28
38
|
logger = logging.getLogger('geoseeq_api')
|
29
39
|
|
@@ -90,3 +100,223 @@ def cli_find_urls_for_reads(state, cores, overwrite, yes, regex, private, module
|
|
90
100
|
groups = _group_files(knex, filepaths, module_name, regex, yes)
|
91
101
|
for file_name, target_url in _find_target_urls(groups, module_name, proj, filepaths, overwrite, cores, state):
|
92
102
|
print(f'{file_name}\t{target_url}', file=state.outfile)
|
103
|
+
|
104
|
+
|
105
|
+
def _get_result_file_from_record_with_ids(knex, record: Dict) -> Dict:
|
106
|
+
"""Get all relevant objects from a record, handling GRNs, UUIDs, and absolute names without requiring parent objects.
|
107
|
+
|
108
|
+
Returns a dict with 'org', 'project', 'sample', 'folder', and 'result_file' keys.
|
109
|
+
Objects may be None if not needed/specified.
|
110
|
+
Guaranteed that at least org is not None.
|
111
|
+
"""
|
112
|
+
objects = {
|
113
|
+
'org': None,
|
114
|
+
'project': None,
|
115
|
+
'sample': None,
|
116
|
+
'folder': None,
|
117
|
+
'result_file': None
|
118
|
+
}
|
119
|
+
|
120
|
+
# Try to get file directly - if it's a GRN/UUID we don't need parent objects
|
121
|
+
try:
|
122
|
+
objects['result_file'] = result_file_from_id(knex, record['filename'])
|
123
|
+
objects['folder'] = objects['result_file'].folder
|
124
|
+
if hasattr(objects['folder'], 'sample'):
|
125
|
+
objects['sample'] = objects['folder'].sample
|
126
|
+
objects['project'] = objects['sample'].project
|
127
|
+
else:
|
128
|
+
objects['project'] = objects['folder'].project
|
129
|
+
objects['org'] = objects['project'].org
|
130
|
+
return objects
|
131
|
+
except ValueError:
|
132
|
+
pass # Not a GRN, UUID or abs name. Continue with normal flow
|
133
|
+
|
134
|
+
# Try to get folder directly - if it's a GRN/UUID we don't need parent objects
|
135
|
+
try:
|
136
|
+
objects['folder'] = result_folder_from_id(knex, record['folder'])
|
137
|
+
# Get parent objects from folder
|
138
|
+
if hasattr(objects['folder'], 'sample'):
|
139
|
+
objects['sample'] = objects['folder'].sample
|
140
|
+
objects['project'] = objects['sample'].project
|
141
|
+
else:
|
142
|
+
objects['project'] = objects['folder'].project
|
143
|
+
objects['org'] = objects['project'].org
|
144
|
+
return objects
|
145
|
+
except ValueError:
|
146
|
+
pass # Not a GRN, UUID or abs name. Continue with normal flow
|
147
|
+
|
148
|
+
# Try to get sample directly if specified
|
149
|
+
if pd.notna(record['sample']):
|
150
|
+
try:
|
151
|
+
objects['sample'] = sample_from_id(knex, record['sample'])
|
152
|
+
objects['project'] = objects['sample'].project
|
153
|
+
objects['org'] = objects['project'].org
|
154
|
+
return objects
|
155
|
+
except ValueError:
|
156
|
+
pass # Not a GRN, UUID or abs name. Continue with normal flow
|
157
|
+
|
158
|
+
# Try to get project directly
|
159
|
+
try:
|
160
|
+
objects['project'] = project_from_id(knex, record['project'])
|
161
|
+
objects['org'] = objects['project'].org
|
162
|
+
return objects
|
163
|
+
except ValueError:
|
164
|
+
pass # Not a GRN/UUID, continue
|
165
|
+
|
166
|
+
|
167
|
+
if objects['org'] is None: # Get org directly if we don't have one yet
|
168
|
+
objects['org'] = org_from_id(knex, record['organization'])
|
169
|
+
|
170
|
+
return objects
|
171
|
+
|
172
|
+
|
173
|
+
def _get_result_file_from_record(knex, record: Dict) -> Dict:
|
174
|
+
"""Get all relevant objects from a record, handling GRNs/UUIDs without requiring parent objects.
|
175
|
+
|
176
|
+
Returns a dict with 'org', 'project', 'sample', 'folder', and 'result_file' keys.
|
177
|
+
Objects may be None if not needed/specified.
|
178
|
+
"""
|
179
|
+
objects = _get_result_file_from_record_with_ids(knex, record)
|
180
|
+
|
181
|
+
if objects['project'] is None:
|
182
|
+
objects['project'] = objects['org'].project(record['project'])
|
183
|
+
|
184
|
+
if objects['sample'] is None:
|
185
|
+
if pd.notna(record['sample']):
|
186
|
+
objects['sample'] = objects['project'].sample(record['sample'])
|
187
|
+
parent = objects['sample']
|
188
|
+
else:
|
189
|
+
parent = objects['project']
|
190
|
+
|
191
|
+
if objects['folder'] is None:
|
192
|
+
objects['folder'] = parent.result_folder(record['folder'])
|
193
|
+
|
194
|
+
if objects['result_file'] is None:
|
195
|
+
objects['result_file'] = objects['folder'].result_file(record['filename'])
|
196
|
+
|
197
|
+
objects['result_file'].idem()
|
198
|
+
print(objects)
|
199
|
+
return objects
|
200
|
+
|
201
|
+
|
202
|
+
def _add_record_to_upload_manager_local_file(record: Dict, result_file, upload_manager: GeoSeeqUploadManager) -> None:
|
203
|
+
"""Add a local file upload to the upload manager."""
|
204
|
+
upload_manager.add_result_file(result_file, record['path'], link_type='upload')
|
205
|
+
|
206
|
+
|
207
|
+
def _add_record_to_upload_manager_s3_file(record: Dict, result_file, upload_manager: GeoSeeqUploadManager) -> None:
|
208
|
+
"""Add an S3 file link to the upload manager.
|
209
|
+
|
210
|
+
Handles two types of S3 URLs:
|
211
|
+
1. https://endpoint/bucket/key - Full URL with endpoint included
|
212
|
+
2. s3://bucket/key - S3 protocol URL that needs endpoint added
|
213
|
+
"""
|
214
|
+
path = record['path']
|
215
|
+
|
216
|
+
if path.startswith('s3://'):
|
217
|
+
# Convert s3:// URL to https:// URL
|
218
|
+
if not record['endpoint_url']:
|
219
|
+
raise ValueError("endpoint_url is required for s3:// URLs")
|
220
|
+
|
221
|
+
# Remove s3:// prefix and combine with endpoint
|
222
|
+
bucket_and_key = path[5:] # len('s3://') == 5
|
223
|
+
path = f"{record['endpoint_url'].rstrip('/')}/{bucket_and_key}"
|
224
|
+
elif not path.startswith('https://'):
|
225
|
+
raise ValueError("S3 URLs must start with either 's3://' or 'https://'")
|
226
|
+
|
227
|
+
upload_manager.add_result_file(result_file, path, link_type='s3')
|
228
|
+
|
229
|
+
|
230
|
+
def _upload_one_record(knex, record: Dict, overwrite: bool, upload_manager: GeoSeeqUploadManager) -> Dict:
|
231
|
+
"""Process a single record from the config file and add it to the upload manager."""
|
232
|
+
objects = _get_result_file_from_record(knex, record)
|
233
|
+
if not objects['result_file']:
|
234
|
+
raise ValueError(f"Could not find or create result_file from record: {record}")
|
235
|
+
|
236
|
+
# Add to upload manager based on type
|
237
|
+
if record['type'].lower() == 'local':
|
238
|
+
_add_record_to_upload_manager_local_file(record, objects["result_file"], upload_manager)
|
239
|
+
elif record['type'].lower() == 's3':
|
240
|
+
_add_record_to_upload_manager_s3_file(record, objects["result_file"], upload_manager)
|
241
|
+
else:
|
242
|
+
raise ValueError(f"Unknown file type: {record['type']}")
|
243
|
+
|
244
|
+
return objects
|
245
|
+
|
246
|
+
|
247
|
+
REQUIRED_COLUMNS = [
|
248
|
+
'organization', 'project', 'sample', 'folder',
|
249
|
+
'filename', 'path', 'type', 'endpoint_url'
|
250
|
+
]
|
251
|
+
|
252
|
+
|
253
|
+
@click.command('from-config')
|
254
|
+
@use_common_state
|
255
|
+
@click.option('--cores', default=1, help='Number of uploads to run in parallel')
|
256
|
+
@click.option('--sep', default=',', help='Separator character for the CSV file')
|
257
|
+
@overwrite_option
|
258
|
+
@yes_option
|
259
|
+
@click.argument('config_file', type=click.Path(exists=True))
|
260
|
+
def cli_upload_from_config(state, cores, sep, overwrite, yes, config_file):
|
261
|
+
"""Upload files to GeoSeeq based on a configuration CSV file.
|
262
|
+
|
263
|
+
\b
|
264
|
+
The CSV file must have the following columns:
|
265
|
+
- organization: Organization name, GRN, or UUID (optional if project/sample/folder specified by GRN/UUID)
|
266
|
+
- project: Project name, GRN, or UUID (optional if sample/folder specified by GRN/UUID)
|
267
|
+
- sample: Sample name, GRN, or UUID (optional, also optional if folder specified by GRN/UUID)
|
268
|
+
- folder: Folder name, GRN, or UUID
|
269
|
+
- filename: Name to give the file on GeoSeeq
|
270
|
+
- path: Path to local file or S3 URL
|
271
|
+
- type: Either "local" or "s3"
|
272
|
+
- endpoint_url: S3 endpoint URL (required for S3 files)
|
273
|
+
|
274
|
+
\b
|
275
|
+
When using GRNs or UUIDs, you can omit the parent object IDs. For example:
|
276
|
+
- If folder is a GRN/UUID, organization/project/sample can be blank
|
277
|
+
- If sample is a GRN/UUID, organization/project can be blank
|
278
|
+
- If project is a GRN/UUID, organization can be blank
|
279
|
+
|
280
|
+
\b
|
281
|
+
Example config.csv:
|
282
|
+
organization,project,sample,folder,filename,path,type,endpoint_url
|
283
|
+
MyOrg,MyProject,Sample1,reads,file1.fastq,/path/to/file1.fastq,local,
|
284
|
+
,grn:project:uuid,Sample2,reads,file2.fastq,/path/to/file2.fastq,local,
|
285
|
+
,,grn:sample:uuid,reads,file3.fastq,/path/to/file3.fastq,local,
|
286
|
+
,,,grn:folder:uuid,file4.fastq,s3://bucket/file4.fastq,s3,https://s3.amazonaws.com
|
287
|
+
|
288
|
+
\b
|
289
|
+
Example with tab separator:
|
290
|
+
$ geoseeq upload advanced from-config --sep $'\t' config.tsv
|
291
|
+
"""
|
292
|
+
knex = state.get_knex()
|
293
|
+
|
294
|
+
# Read and validate config file
|
295
|
+
df = pd.read_csv(config_file, sep=sep)
|
296
|
+
missing_cols = set(REQUIRED_COLUMNS) - set(df.columns)
|
297
|
+
if missing_cols:
|
298
|
+
raise click.UsageError(f"Config file missing required columns: {missing_cols}")
|
299
|
+
|
300
|
+
# Create upload manager
|
301
|
+
upload_manager = GeoSeeqUploadManager(
|
302
|
+
n_parallel_uploads=cores,
|
303
|
+
progress_tracker_factory=PBarManager().get_new_bar,
|
304
|
+
log_level=state.log_level,
|
305
|
+
overwrite=overwrite,
|
306
|
+
use_cache=state.use_cache,
|
307
|
+
)
|
308
|
+
|
309
|
+
# Process records and add to upload manager
|
310
|
+
objects_by_record = {} # Store objects for human readable paths
|
311
|
+
for _, record in df.iterrows():
|
312
|
+
objects = _upload_one_record(knex, record, overwrite, upload_manager)
|
313
|
+
objects_by_record[record['path']] = objects
|
314
|
+
|
315
|
+
# Show preview with both technical and human readable paths
|
316
|
+
click.echo(upload_manager.get_preview_string(), err=True)
|
317
|
+
|
318
|
+
if not yes:
|
319
|
+
click.confirm('Do you want to proceed with these uploads?', abort=True)
|
320
|
+
|
321
|
+
# Perform uploads
|
322
|
+
upload_manager.upload_files()
|
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
import click
|
3
3
|
import requests
|
4
4
|
from os.path import basename
|
5
|
-
|
5
|
+
import pandas as pd
|
6
6
|
from multiprocessing import Pool, current_process
|
7
7
|
|
8
8
|
from geoseeq.cli.constants import *
|
@@ -67,8 +67,12 @@ def _get_regex(knex, filepaths, module_name, lib, regex):
|
|
67
67
|
return regex
|
68
68
|
|
69
69
|
|
70
|
-
def _group_files(knex, filepaths, module_name, regex, yes):
|
70
|
+
def _group_files(knex, filepaths, module_name, regex, yes, name_map):
|
71
71
|
"""Group the files into samples, confirm, and return the groups."""
|
72
|
+
if name_map is not None:
|
73
|
+
name_map_filename, cur_col, new_col = name_map
|
74
|
+
name_map = pd.read_csv(name_map_filename)[[cur_col, new_col]]
|
75
|
+
name_map = name_map.set_index(cur_col).to_dict()
|
72
76
|
seq_length, seq_type = module_name.split('::')[:2]
|
73
77
|
groups = knex.post('bulk_upload/group_files', json={
|
74
78
|
'filenames': list(filepaths.keys()),
|
@@ -76,7 +80,11 @@ def _group_files(knex, filepaths, module_name, regex, yes):
|
|
76
80
|
'regex': regex
|
77
81
|
})
|
78
82
|
for group in groups:
|
79
|
-
|
83
|
+
sample_name = group["sample_name"]
|
84
|
+
if name_map:
|
85
|
+
sample_name = name_map.get(sample_name, sample_name)
|
86
|
+
group["sample_name"] = sample_name
|
87
|
+
click.echo(f'sample_name: {sample_name}', err=True)
|
80
88
|
click.echo(f' module_name: {module_name}', err=True)
|
81
89
|
for field_name, filename in group['fields'].items():
|
82
90
|
path = filepaths[filename]
|
@@ -173,10 +181,11 @@ def flatten_list_of_bams(filepaths):
|
|
173
181
|
@private_option
|
174
182
|
@link_option
|
175
183
|
@no_new_versions_option
|
184
|
+
@click.option('--name-map', default=None, nargs=3, help="A file to use for converting names. Takes three arguments: a file name, a column name for current names, and a column name for new names.")
|
176
185
|
@module_option(FASTQ_MODULE_NAMES)
|
177
186
|
@project_id_arg
|
178
187
|
@click.argument('fastq_files', type=click.Path(exists=True), nargs=-1)
|
179
|
-
def cli_upload_reads_wizard(state, cores, overwrite, yes, regex, private, link_type, no_new_versions, module_name, project_id, fastq_files):
|
188
|
+
def cli_upload_reads_wizard(state, cores, overwrite, yes, regex, private, link_type, no_new_versions, name_map, module_name, project_id, fastq_files):
|
180
189
|
"""Upload fastq read files to GeoSeeq.
|
181
190
|
|
182
191
|
This command automatically groups files by their sample name, lane number
|
geoseeq/constants.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from os import environ
|
2
2
|
from os.path import join
|
3
|
+
from typing import Literal
|
3
4
|
|
4
5
|
FIVE_MB = 5 * (1024 ** 2)
|
5
6
|
FASTQ_MODULE_NAMES = [
|
@@ -13,4 +14,6 @@ DEFAULT_ENDPOINT = "https://backend.geoseeq.com"
|
|
13
14
|
|
14
15
|
CONFIG_FOLDER = environ.get("XDG_CONFIG_HOME", join(environ["HOME"], ".config"))
|
15
16
|
CONFIG_DIR = environ.get("GEOSEEQ_CONFIG_DIR", join(CONFIG_FOLDER, "geoseeq"))
|
16
|
-
PROFILES_PATH = join(CONFIG_DIR, "profiles.json")
|
17
|
+
PROFILES_PATH = join(CONFIG_DIR, "profiles.json")
|
18
|
+
|
19
|
+
OBJECT_TYPE_STR = Literal['org', 'project', 'sample', 'sample_result_folder', 'project_result_folder', 'sample_result_file', 'project_result_file']
|
@@ -22,51 +22,51 @@ from .from_names import (
|
|
22
22
|
project_result_file_from_name,
|
23
23
|
result_file_from_name,
|
24
24
|
)
|
25
|
-
from .utils import is_grn_or_uuid, is_name
|
25
|
+
from .utils import is_grn_or_uuid, is_name, is_abs_name
|
26
26
|
from geoseeq.knex import with_knex
|
27
|
-
|
27
|
+
from geoseeq.constants import OBJECT_TYPE_STR
|
28
28
|
logger = logging.getLogger("geoseeq_api") # Same name as calling module
|
29
29
|
|
30
30
|
|
31
|
-
def _generic_from_id(knex, id, from_uuid_func, from_name_func):
|
31
|
+
def _generic_from_id(knex, id, from_uuid_func, from_name_func, object_type_str: OBJECT_TYPE_STR):
|
32
32
|
"""Return the object which the id points to."""
|
33
33
|
logger.debug(f'Getting object from id: {id}, knex: {knex}, from_uuid_func: {from_uuid_func}, from_name_func: {from_name_func}')
|
34
34
|
if is_grn_or_uuid(id):
|
35
35
|
id = id.split(':')[-1] # if this is a GRN, get the UUID. Won't hurt if it's already a UUID.
|
36
36
|
return from_uuid_func(knex, id)
|
37
|
-
if
|
37
|
+
if is_abs_name(id, object_type_str):
|
38
38
|
return from_name_func(knex, id)
|
39
|
-
raise ValueError(f'"{id}" is not a GRN, UUID, or name')
|
39
|
+
raise ValueError(f'"{id}" is not a GRN, UUID, or absolute name for {object_type_str}')
|
40
40
|
|
41
41
|
|
42
42
|
@with_knex
|
43
43
|
def org_from_id(knex, id):
|
44
44
|
"""Return the organization object which the id points to."""
|
45
|
-
return _generic_from_id(knex, id, org_from_uuid, org_from_name)
|
45
|
+
return _generic_from_id(knex, id, org_from_uuid, org_from_name, 'org')
|
46
46
|
|
47
47
|
|
48
48
|
@with_knex
|
49
49
|
def project_from_id(knex, id):
|
50
50
|
"""Return the project object which the id points to."""
|
51
|
-
return _generic_from_id(knex, id, project_from_uuid, project_from_name)
|
51
|
+
return _generic_from_id(knex, id, project_from_uuid, project_from_name, 'project')
|
52
52
|
|
53
53
|
|
54
54
|
@with_knex
|
55
55
|
def sample_from_id(knex, id):
|
56
56
|
"""Return the sample object which the id points to."""
|
57
|
-
return _generic_from_id(knex, id, sample_from_uuid, sample_from_name)
|
57
|
+
return _generic_from_id(knex, id, sample_from_uuid, sample_from_name, 'sample')
|
58
58
|
|
59
59
|
|
60
60
|
@with_knex
|
61
61
|
def sample_result_folder_from_id(knex, id):
|
62
62
|
"""Return the sample result folder object which the id points to."""
|
63
|
-
return _generic_from_id(knex, id, sample_result_folder_from_uuid, sample_result_folder_from_name)
|
63
|
+
return _generic_from_id(knex, id, sample_result_folder_from_uuid, sample_result_folder_from_name, 'sample_result_folder')
|
64
64
|
|
65
65
|
|
66
66
|
@with_knex
|
67
67
|
def project_result_folder_from_id(knex, id):
|
68
68
|
"""Return the project result folder object which the id points to."""
|
69
|
-
return _generic_from_id(knex, id, project_result_folder_from_uuid, project_result_folder_from_name)
|
69
|
+
return _generic_from_id(knex, id, project_result_folder_from_uuid, project_result_folder_from_name, 'project_result_folder')
|
70
70
|
|
71
71
|
|
72
72
|
@with_knex
|
@@ -75,19 +75,22 @@ def result_folder_from_id(knex, id):
|
|
75
75
|
|
76
76
|
Guess the result folder is a sample result folder. If not, try a project result folder.
|
77
77
|
"""
|
78
|
-
|
78
|
+
try:
|
79
|
+
return _generic_from_id(knex, id, result_folder_from_uuid, result_folder_from_name, 'sample_result_folder')
|
80
|
+
except ValueError:
|
81
|
+
return _generic_from_id(knex, id, result_folder_from_uuid, result_folder_from_name, 'project_result_folder')
|
79
82
|
|
80
83
|
|
81
84
|
@with_knex
|
82
85
|
def sample_result_file_from_id(knex, id):
|
83
86
|
"""Return the sample result file object which the id points to."""
|
84
|
-
return _generic_from_id(knex, id, sample_result_file_from_uuid, sample_result_file_from_name)
|
87
|
+
return _generic_from_id(knex, id, sample_result_file_from_uuid, sample_result_file_from_name, 'sample_result_file')
|
85
88
|
|
86
89
|
|
87
90
|
@with_knex
|
88
91
|
def project_result_file_from_id(knex, id):
|
89
92
|
"""Return the project result file object which the id points to."""
|
90
|
-
return _generic_from_id(knex, id, project_result_file_from_uuid, project_result_file_from_name)
|
93
|
+
return _generic_from_id(knex, id, project_result_file_from_uuid, project_result_file_from_name, 'project_result_file')
|
91
94
|
|
92
95
|
|
93
96
|
@with_knex
|
@@ -96,7 +99,10 @@ def result_file_from_id(knex, id):
|
|
96
99
|
|
97
100
|
Guess the result file is a sample result file. If not, try a project result file.
|
98
101
|
"""
|
99
|
-
|
102
|
+
try:
|
103
|
+
return _generic_from_id(knex, id, result_file_from_uuid, result_file_from_name, 'sample_result_file')
|
104
|
+
except ValueError:
|
105
|
+
return _generic_from_id(knex, id, result_file_from_uuid, result_file_from_name, 'project_result_file')
|
100
106
|
|
101
107
|
|
102
108
|
|
geoseeq/id_constructors/utils.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import uuid
|
2
|
+
from geoseeq.constants import OBJECT_TYPE_STR
|
2
3
|
|
3
4
|
|
4
5
|
def is_grn(el):
|
@@ -29,6 +30,22 @@ def is_name(el):
|
|
29
30
|
return False
|
30
31
|
|
31
32
|
|
33
|
+
def is_abs_name(el, object_type_str: OBJECT_TYPE_STR) -> bool:
|
34
|
+
"""Return True if `el` is an absolute name for the given object type."""
|
35
|
+
if is_grn_or_uuid(el):
|
36
|
+
return False
|
37
|
+
n_required_slashes = {
|
38
|
+
'org': 0,
|
39
|
+
'project': 1,
|
40
|
+
'sample': 2,
|
41
|
+
'sample_result_folder': 3,
|
42
|
+
'sample_result_file': 4,
|
43
|
+
'project_result_folder': 2,
|
44
|
+
'project_result_file': 3,
|
45
|
+
}[object_type_str]
|
46
|
+
return el.count('/') == n_required_slashes
|
47
|
+
|
48
|
+
|
32
49
|
def is_grn_or_uuid(el):
|
33
50
|
"""Return True if `el` is a GRN or a UUID"""
|
34
51
|
return is_grn(el) or is_uuid(el)
|
geoseeq/result/result_file.py
CHANGED
@@ -18,9 +18,10 @@ from geoseeq.knex import GeoseeqOtherError
|
|
18
18
|
from .utils import *
|
19
19
|
from .file_upload import ResultFileUpload
|
20
20
|
from .file_download import ResultFileDownload
|
21
|
+
from .smart_objects import ResultFileSmartObjects
|
21
22
|
|
22
23
|
|
23
|
-
class ResultFile(RemoteObject, ResultFileUpload, ResultFileDownload):
|
24
|
+
class ResultFile(RemoteObject, ResultFileUpload, ResultFileDownload, ResultFileSmartObjects):
|
24
25
|
remote_fields = [
|
25
26
|
"uuid",
|
26
27
|
"created_at",
|
geoseeq/result/result_folder.py
CHANGED
@@ -225,6 +225,10 @@ class SampleResultFolder(ResultFolder, SampleBioInfoFolder):
|
|
225
225
|
|
226
226
|
def get_fields(self, *args, **kwargs):
|
227
227
|
return self.get_result_files(*args, **kwargs)
|
228
|
+
|
229
|
+
@property
|
230
|
+
def project(self):
|
231
|
+
return self.sample.project
|
228
232
|
|
229
233
|
def __str__(self):
|
230
234
|
return f"<Geoseeq::SampleResultFolder {self.module_name} {self.replicate} {self.uuid} />"
|
@@ -288,6 +292,10 @@ class ProjectResultFolder(ResultFolder):
|
|
288
292
|
|
289
293
|
def get_fields(self, *args, **kwargs):
|
290
294
|
return self.get_result_files(*args, **kwargs)
|
295
|
+
|
296
|
+
@property
|
297
|
+
def project(self):
|
298
|
+
return self.grp
|
291
299
|
|
292
300
|
def __str__(self):
|
293
301
|
return f"<Geoseeq::ProjectResultFolder {self.module_name} {self.replicate} {self.uuid} />"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class ResultFileSmartObjects:
|
4
|
+
"""Interface for ResultFiles to easily create and use smart objects like tables"""
|
5
|
+
|
6
|
+
def is_smart_table(self):
|
7
|
+
"""Return True iff this ResultFile is a smart table."""
|
8
|
+
return self.stored_data.get("__type__") == "smartTable"
|
9
|
+
|
10
|
+
def as_smart_table(self, description=""):
|
11
|
+
"""Return a SmartTable object for this ResultFile. Will create a new SmartTable if it doesn't exist yet."""
|
12
|
+
if self.exists() and not self.is_smart_table():
|
13
|
+
raise ValueError("ResultFile exists but is not a smart table")
|
14
|
+
from geoseeq.smart_table import SmartTable # import here to avoid circular import
|
15
|
+
if self.exists(): # already a smart table
|
16
|
+
smart_table = SmartTable(self.knex, self.name, connected_file_id=self.uuid).get()
|
17
|
+
else: # create a new smart table
|
18
|
+
smart_table = SmartTable(self.knex, self.name, connected_file_id=self.uuid, description=description)
|
19
|
+
smart_table.create(self.parent)
|
20
|
+
return smart_table
|
21
|
+
|
22
|
+
def is_smart_tree(self):
|
23
|
+
"""Return True iff this ResultFile is a smart tree."""
|
24
|
+
return self.stored_data.get("__type__") == "phylodynamics"
|
25
|
+
|
26
|
+
def as_smart_tree(self, newick_str=None, sample_name_id_map=None):
|
27
|
+
"""Return a SmartTree object for this ResultFile. Will create a new SmartTree if it doesn't exist yet."""
|
28
|
+
if self.exists() and not self.is_smart_tree():
|
29
|
+
raise ValueError("ResultFile exists but is not a smart tree")
|
30
|
+
from geoseeq.smart_tree import SmartTree # import here to avoid circular import
|
31
|
+
if self.exists(): # already a smart tree
|
32
|
+
smart_tree = SmartTree.from_blob(self, self.stored_data)
|
33
|
+
else: # create a new smart tree
|
34
|
+
smart_tree = SmartTree(self)
|
35
|
+
if newick_str is not None:
|
36
|
+
smart_tree.create_from_newick(newick_str, sample_name_id_map)
|
37
|
+
smart_tree.create()
|
38
|
+
return smart_tree
|
39
|
+
|
40
|
+
|
geoseeq/smart_table.py
CHANGED
@@ -53,11 +53,12 @@ class SmartTable(RemoteObject):
|
|
53
53
|
|
54
54
|
without_default_columns: if False the server creates 3 example columns.
|
55
55
|
"""
|
56
|
-
|
56
|
+
if description:
|
57
|
+
self.description = description
|
57
58
|
data = {
|
58
59
|
"name": self.name,
|
59
60
|
"folder_id": result_folder.uuid,
|
60
|
-
"description": description,
|
61
|
+
"description": self.description,
|
61
62
|
}
|
62
63
|
url = f"table?without_default_columns={without_default_columns}"
|
63
64
|
blob = self.knex.post(url, json=data)
|
geoseeq/smart_tree.py
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
class SmartTree:
|
2
|
+
|
3
|
+
def __init__(self, result_file):
|
4
|
+
self.result_file = result_file # the result file that contains the smart tree
|
5
|
+
self.content = None
|
6
|
+
|
7
|
+
def create_from_newick(self, newick_str, sample_name_id_map=None):
|
8
|
+
self.content = {
|
9
|
+
"__type__": "phylodynamics",
|
10
|
+
"version": "1.0",
|
11
|
+
"tree": {
|
12
|
+
"value": newick_str,
|
13
|
+
"kind": "newick",
|
14
|
+
"sampleNameIdMap": {} if sample_name_id_map is None else sample_name_id_map,
|
15
|
+
}
|
16
|
+
}
|
17
|
+
return self
|
18
|
+
|
19
|
+
def add_sample_to_map(self, sample):
|
20
|
+
if self.content is None:
|
21
|
+
raise ValueError("Must create tree before adding samples")
|
22
|
+
self.content["tree"]["sampleNameIdMap"][sample.name] = sample.uuid
|
23
|
+
return self
|
24
|
+
|
25
|
+
def add_all_samples_to_map(self, project):
|
26
|
+
for sample in project.get_samples():
|
27
|
+
self.add_sample_to_map(sample)
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def from_blob(cls, result_file, blob):
|
31
|
+
smart_tree = cls(result_file)
|
32
|
+
smart_tree.content = blob
|
33
|
+
return smart_tree
|
34
|
+
|
35
|
+
def save(self):
|
36
|
+
if self.content is None:
|
37
|
+
raise ValueError("Must create tree before saving")
|
38
|
+
self.result_file.upload_json(self.content)
|
39
|
+
return self
|
40
|
+
|
41
|
+
def idem(self):
|
42
|
+
if self.content is None:
|
43
|
+
raise ValueError("Must create tree before saving")
|
44
|
+
self.result_file.upload_json(self.content)
|
45
|
+
return self
|
46
|
+
|
47
|
+
def get(self):
|
48
|
+
self.result_file.get()
|
49
|
+
self.content = self.result_file.stored_data
|
50
|
+
return self
|
51
|
+
|
52
|
+
def create(self):
|
53
|
+
if self.content is None:
|
54
|
+
raise ValueError("Must create tree before saving")
|
55
|
+
self.result_file.upload_json(self.content)
|
56
|
+
return self
|
57
|
+
|
@@ -77,8 +77,17 @@ class GeoSeeqUploadManager:
|
|
77
77
|
self.chunk_size_mb = chunk_size_mb
|
78
78
|
self.use_atomic_upload = use_atomic_upload
|
79
79
|
|
80
|
-
def add_result_file(self, result_file, local_path):
|
81
|
-
|
80
|
+
def add_result_file(self, result_file, local_path, link_type=None):
|
81
|
+
"""Add a result file to be uploaded or linked.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
result_file: A ResultFile object to upload to or link
|
85
|
+
local_path: Path to local file or URL to link
|
86
|
+
link_type: One of 'upload', 's3', 'ftp', 'azure', 'sra', 'http'
|
87
|
+
"""
|
88
|
+
if link_type is None:
|
89
|
+
link_type = self.link_type
|
90
|
+
self._result_files.append((result_file, local_path, link_type))
|
82
91
|
|
83
92
|
def add_local_file_to_result_folder(self, result_folder, local_path, geoseeq_file_name=None):
|
84
93
|
if not geoseeq_file_name:
|
@@ -96,19 +105,20 @@ class GeoSeeqUploadManager:
|
|
96
105
|
|
97
106
|
def get_preview_string(self):
|
98
107
|
out = ["Upload Preview:"]
|
99
|
-
for result_file, local_path in self._result_files:
|
100
|
-
|
108
|
+
for result_file, local_path, link_type in self._result_files:
|
109
|
+
action = "link" if link_type != 'upload' else "upload"
|
110
|
+
out.append(f"{local_path} -> {result_file} ({action})")
|
101
111
|
return "\n".join(out)
|
102
112
|
|
103
113
|
def upload_files(self):
|
104
114
|
upload_args = [(
|
105
115
|
result_file, local_path,
|
106
116
|
self.session, self.progress_tracker_factory(local_path),
|
107
|
-
|
117
|
+
link_type, self.overwrite, self.log_level,
|
108
118
|
self.n_parallel_uploads > 1, self.use_cache, self.no_new_versions,
|
109
119
|
self.threads_per_upload, self.num_retries, self.ignore_errors,
|
110
120
|
self.chunk_size_mb, self.use_atomic_upload
|
111
|
-
) for result_file, local_path in self._result_files
|
121
|
+
) for result_file, local_path, link_type in self._result_files
|
112
122
|
]
|
113
123
|
out = []
|
114
124
|
if self.n_parallel_uploads == 1:
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: geoseeq
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: GeoSeeq command line tools and python API
|
5
5
|
Author: David C. Danko
|
6
6
|
Author-email: "David C. Danko" <dcdanko@biotia.io>
|
@@ -18,6 +18,7 @@ Requires-Dist: pandas
|
|
18
18
|
Requires-Dist: biopython
|
19
19
|
Requires-Dist: tqdm
|
20
20
|
Dynamic: author
|
21
|
+
Dynamic: license-file
|
21
22
|
|
22
23
|
# Geoseeq API Client
|
23
24
|
|
@@ -2,7 +2,7 @@ geoseeq/__init__.py,sha256=YqYqeHbqjgWI5OBIxkPXNvLISOjVWaNwVFy6AGJ7uwc,982
|
|
2
2
|
geoseeq/app.py,sha256=Y6d1UzxFLfE3RNccATbFCVi6kH3eFmzwoUbeR2Ry09A,2387
|
3
3
|
geoseeq/blob_constructors.py,sha256=AkWpDQY0EdGMxF1p6eRspyHKubcUdiW4it-_Q7S2QWk,188
|
4
4
|
geoseeq/bulk_creators.py,sha256=pdn-Dv7yv5SFv-PfDuQbuOnw2W4-BfIfRJVRAhM8U6s,2115
|
5
|
-
geoseeq/constants.py,sha256=
|
5
|
+
geoseeq/constants.py,sha256=KmWH3gwHLd3toTWLWSViV_1eAYzPOPZ5bv6INpgwzQI,665
|
6
6
|
geoseeq/file_system_cache.py,sha256=HzVZWtwLD2fjWWSo_UfWmGeBltm9He4lP_OqzKwNGWg,4138
|
7
7
|
geoseeq/knex.py,sha256=zcjafsmUn9SC3LlRnvvaXpr-pHYZ0IXk7LpzuUoE3MI,8312
|
8
8
|
geoseeq/organization.py,sha256=bJkYL8_D-k6IYAaii2ZbxjwYnXy6lvu6iLXscxKlA3w,2542
|
@@ -11,8 +11,9 @@ geoseeq/project.py,sha256=Ba3yvrSyPVnEM5_VGSQu6YPH3CTwE3fHs4JjAepEK7g,13880
|
|
11
11
|
geoseeq/remote_object.py,sha256=GYN6PKU7Zz3htIdpFjfZiFejzGqqJHbJyKlefM1Eixk,7151
|
12
12
|
geoseeq/sample.py,sha256=HAfMiDPHp1UJgIA2lI6oGnNit4YKyj7nx9X07CCN98U,8316
|
13
13
|
geoseeq/search.py,sha256=gawad6Cx5FxJBPlYkXWb-UKAO-UC0_yhvyU9Ca1kaNI,3388
|
14
|
-
geoseeq/smart_table.py,sha256=
|
15
|
-
geoseeq/
|
14
|
+
geoseeq/smart_table.py,sha256=rihMsFUIn-vn4w6ukVZTHI9bjDSEr8xHExBfX8mwCHM,6169
|
15
|
+
geoseeq/smart_tree.py,sha256=bSjDlwmOuNXutYJhytA1RovwRCHV6ZxXXJPiIGFhPaA,1825
|
16
|
+
geoseeq/upload_download_manager.py,sha256=DxzwhkpuzuXTQt28ORULspDi2rzXqqIHqROEAs99SxQ,9849
|
16
17
|
geoseeq/user.py,sha256=tol8i1UGLRrbMw5jeJDnna1ikRgrCDd50Jxz0a1lSgg,690
|
17
18
|
geoseeq/utils.py,sha256=ZXpWb2MetUIeLrExiXb7IaOXYrW1pvrdP3o0KWzbwCs,4035
|
18
19
|
geoseeq/work_orders.py,sha256=5uLVVfdKE8qh4gGaHkdBpXJGRTujuSg59knWCqEET4A,8071
|
@@ -20,11 +21,11 @@ geoseeq/cli/__init__.py,sha256=4WnK87K5seRK3SGJAxNWnQTqyg5uBhdhrOrzB1D4b3M,24
|
|
20
21
|
geoseeq/cli/constants.py,sha256=Do5AUf9lMO9_P8KpFJ3XwwFBAWsxSjZ6sx9_QEGyC_c,176
|
21
22
|
geoseeq/cli/copy.py,sha256=02U9kdrAIbbM8MlRMLL6p-LMYFSuRObE3h5jyvcL__M,2275
|
22
23
|
geoseeq/cli/detail.py,sha256=q8Suu-j2k18knfSVFG-SWWGNsKM-n8y9RMA3LcIIi9Y,4132
|
23
|
-
geoseeq/cli/download.py,sha256=
|
24
|
+
geoseeq/cli/download.py,sha256=Znjuc9IFOcIa5_Od9mFXHJdYAJtgw9Bc_wPPcOVXn7s,21298
|
24
25
|
geoseeq/cli/fastq_utils.py,sha256=-bmeQLaiMBm57zWOF0R5OlWTU0_3sh1JBC1RYw2BOFM,3083
|
25
26
|
geoseeq/cli/find_grn.py,sha256=oMDxkzGQBQb2_cCuvmwoeHOsFHqyO9RLeJzrB6bAe5M,439
|
26
27
|
geoseeq/cli/get_eula.py,sha256=79mbUwyiF7O1r0g6UTxG9kJGQEqKuH805E6eLkPC6Y4,997
|
27
|
-
geoseeq/cli/main.py,sha256=
|
28
|
+
geoseeq/cli/main.py,sha256=pkpjc_5dZ8T0F92777OKRXT_EFoHelfw4DhKjEcXqIE,3982
|
28
29
|
geoseeq/cli/manage.py,sha256=wGXAcVaXqE5JQEU8Jh6OlHr02nB396bpS_SFcOZdrEo,5929
|
29
30
|
geoseeq/cli/progress_bar.py,sha256=p1Xl01nkYxSBZCB30ue2verIIi22W93m3ZAMAxipD0g,738
|
30
31
|
geoseeq/cli/project.py,sha256=V5SdXm2Hwo2lxrkpwRDedw-mAE4XnM2uwT-Gj1D90VQ,3030
|
@@ -40,10 +41,10 @@ geoseeq/cli/shared_params/config.py,sha256=HQ0xQh_jdt3EKI5VXYqQXzo-s8Rm6YlziMyVX
|
|
40
41
|
geoseeq/cli/shared_params/id_handlers.py,sha256=KtzflnplYVkXsyqI5Ej6r-_BwQnuXVHPr7JcYumTKNc,10700
|
41
42
|
geoseeq/cli/shared_params/obj_getters.py,sha256=ZSkt6LnDkVFlNVYKgLrjzg60-6BthZMr3eeD3HNqzac,2741
|
42
43
|
geoseeq/cli/shared_params/opts_and_args.py,sha256=_DcJ-TqgrbBaeDd-kuHEx2gLZPQN6EHZYWh8Ag-d8Vg,2091
|
43
|
-
geoseeq/cli/upload/__init__.py,sha256=
|
44
|
-
geoseeq/cli/upload/upload.py,sha256=
|
45
|
-
geoseeq/cli/upload/upload_advanced.py,sha256=
|
46
|
-
geoseeq/cli/upload/upload_reads.py,sha256=
|
44
|
+
geoseeq/cli/upload/__init__.py,sha256=tRrljqG7uBM6zVBKpDw1zevHz6Vy418HuIYZKwByyWg,862
|
45
|
+
geoseeq/cli/upload/upload.py,sha256=U9w18sG6dZ0uJoAMcQDLdrEtgmRaSaPS7-8KM5LkVd4,14220
|
46
|
+
geoseeq/cli/upload/upload_advanced.py,sha256=1pjtbe8EidIo4eR1_oVD00rPCTxYj8CRqB8D9buA62M,12432
|
47
|
+
geoseeq/cli/upload/upload_reads.py,sha256=m9ujcjSYOG88LhxMj1vEt8v7FpuhRciL4ntE6oNlVuM,11632
|
47
48
|
geoseeq/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
48
49
|
geoseeq/contrib/ncbi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
50
|
geoseeq/contrib/ncbi/api.py,sha256=WQeLoGA_-Zha-QeSO8_i7HpvXyD8UkV0qc5okm11KiA,1056
|
@@ -52,11 +53,11 @@ geoseeq/contrib/ncbi/cli.py,sha256=j9zEcaZPTryK3a4xluRxigcJKDhRpRxbp3KZSx-Bfhk,2
|
|
52
53
|
geoseeq/contrib/ncbi/setup_logging.py,sha256=Tp1bY1U0f-o739aHpvVYriG2qdd1lFvCYBXZeXQgt-w,175
|
53
54
|
geoseeq/id_constructors/__init__.py,sha256=w5E0PNQ9UuAxBeZbDI7KBnUoERd85gGz3nScz45bd2o,126
|
54
55
|
geoseeq/id_constructors/from_blobs.py,sha256=wZp6P6m7X0lhZAQxorHKJQn1CTBvKyfANzu2VFexp44,6315
|
55
|
-
geoseeq/id_constructors/from_ids.py,sha256=
|
56
|
+
geoseeq/id_constructors/from_ids.py,sha256=SRylajToFdnRON5EhPIvcAi64XuIzseaasZlOooqTpg,3984
|
56
57
|
geoseeq/id_constructors/from_names.py,sha256=RqgFjDsAwQcidMkZwX7oB00OvBAKTiilHYetTPogJ40,4174
|
57
58
|
geoseeq/id_constructors/from_uuids.py,sha256=loqTeZqDZuvfVVHpS7m-Y_spNaY3h9VRjt0wDiMxHbs,3522
|
58
59
|
geoseeq/id_constructors/resolvers.py,sha256=8hp5xJSCoZrAXtMT54Hp4okt63l909XqJU3IQx-VCgc,2676
|
59
|
-
geoseeq/id_constructors/utils.py,sha256=
|
60
|
+
geoseeq/id_constructors/utils.py,sha256=Up_vXoZDwa2mJGllJ6CmcbaOGUvA-sM0gMmFnXK8wn4,1255
|
60
61
|
geoseeq/plotting/__init__.py,sha256=RkGoXxgu7jEfK0B7NmdalPS2AbU7I7dZwDbi4rn9CKM,154
|
61
62
|
geoseeq/plotting/constants.py,sha256=CGUlm8WAFG3YRKdicc9Rcy5hFxUdUm2RgK0iXZWLuX8,285
|
62
63
|
geoseeq/plotting/highcharts.py,sha256=AGzdW4VSUsL_rfEI-RiVbbtaTLFMARvzLzVUDrKTlnU,4096
|
@@ -70,10 +71,11 @@ geoseeq/result/bioinfo.py,sha256=QQtbyogrdro9avJSN0713sxLVnVeA24mFw3hWtKDKyw,178
|
|
70
71
|
geoseeq/result/file_chunker.py,sha256=bXq1csuRtqMB5sbH-AfWo6gdPwrivv5DJPuHVj-h08w,1758
|
71
72
|
geoseeq/result/file_download.py,sha256=5IXg_dIWlrRHBJQssO42da5_bIJOyH0_b8K2KWVAFBE,8210
|
72
73
|
geoseeq/result/file_upload.py,sha256=xs1DrI-h4ZP7xN8HPBc3SFpcPAxR5HAolraP1Zu7tvE,10648
|
73
|
-
geoseeq/result/result_file.py,sha256=
|
74
|
-
geoseeq/result/result_folder.py,sha256
|
74
|
+
geoseeq/result/result_file.py,sha256=mkFh2DpKO1-kEAARCMYjkc7TmkJh41azyauGIHl_VZo,9173
|
75
|
+
geoseeq/result/result_folder.py,sha256=iyO0hwZWokrH6oWhBgHlunWMpCMpejKb8v2sHFhecws,11283
|
75
76
|
geoseeq/result/resumable_download_tracker.py,sha256=YEzqHBBnE7L3XokTvlTAhHZ8TcDTIE_pyTQ7YadOfbU,3667
|
76
77
|
geoseeq/result/resumable_upload_tracker.py,sha256=2aI09gYz2yw63jEXqs8lmCRKQ79TIc3YuPETvP0Jeek,3811
|
78
|
+
geoseeq/result/smart_objects.py,sha256=-krK9h9HcznIglf189uPghOidF_BzOeSxmBWxrtFFo8,1991
|
77
79
|
geoseeq/result/utils.py,sha256=C-CxGzB3WddlnRiqFSkrY78I_m0yFgNqsTBRzGU-y8Q,2772
|
78
80
|
geoseeq/vc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
81
|
geoseeq/vc/checksum.py,sha256=y8rh1asUZNbE_NLiFO0-9hImLNiTOc2YXQBRKORWK7k,710
|
@@ -84,12 +86,12 @@ geoseeq/vc/vc_cache.py,sha256=P4LXTbq2zOIv1OhP7Iw5MmypR2vXuy29Pq5K6gRvi-M,730
|
|
84
86
|
geoseeq/vc/vc_dir.py,sha256=A9CLTh2wWCRzZjiLyqXD1vhtsWZGD3OjaMT5KqlfAXI,457
|
85
87
|
geoseeq/vc/vc_sample.py,sha256=qZeioWydXvfu4rGMs20nICfNcp46y_XkND-bHdV6P5M,3850
|
86
88
|
geoseeq/vc/vc_stub.py,sha256=IQr8dI0zsWKVAeY_5ybDD6n49_3othcgfHS3P0O9tuY,3110
|
89
|
+
geoseeq-0.7.0.dist-info/licenses/LICENSE,sha256=IuhIl1XCxXLPLJT_coN1CNqQU4Khlq7x4IdW7ioOJD8,1067
|
87
90
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
88
91
|
tests/test_api_client.py,sha256=TS5njc5pcPP_Ycy-ljcfPVT1hQRBsFVdQ0lCqBmoesU,12810
|
89
92
|
tests/test_plotting.py,sha256=TcTu-2ARr8sxZJ7wPQxmbs3-gHw7uRvsgrhhhg0qKik,784
|
90
|
-
geoseeq-0.
|
91
|
-
geoseeq-0.
|
92
|
-
geoseeq-0.
|
93
|
-
geoseeq-0.
|
94
|
-
geoseeq-0.
|
95
|
-
geoseeq-0.6.14.dev7.dist-info/RECORD,,
|
93
|
+
geoseeq-0.7.0.dist-info/METADATA,sha256=lEr_Lja53CYN53DTQTR3CKG3cqPdwgnuB03sbiwtzTs,4953
|
94
|
+
geoseeq-0.7.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
95
|
+
geoseeq-0.7.0.dist-info/entry_points.txt,sha256=yF-6KDM8zXib4Al0qn49TX-qM7PUkWUIcYtsgt36rjM,45
|
96
|
+
geoseeq-0.7.0.dist-info/top_level.txt,sha256=zZk7mmeaqAYqFJG8nq2DTgSQPbflRjJwkDIhNURPDEU,14
|
97
|
+
geoseeq-0.7.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|