comparison env/lib/python3.9/site-packages/bioblend/galaxy/libraries/__init__.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """
2 Contains possible interactions with the Galaxy Data Libraries
3 """
4 import logging
5 import time
6
7 from bioblend.galaxy.client import Client
8 from bioblend.galaxy.datasets import (
9 DatasetTimeoutException,
10 TERMINAL_STATES,
11 )
12 from bioblend.util import attach_file
13
14 log = logging.getLogger(__name__)
15
16
17 class LibraryClient(Client):
18
19 def __init__(self, galaxy_instance):
20 self.module = 'libraries'
21 super().__init__(galaxy_instance)
22
23 def create_library(self, name, description=None, synopsis=None):
24 """
25 Create a data library with the properties defined in the arguments.
26
27 :type name: str
28 :param name: Name of the new data library
29
30 :type description: str
31 :param description: Optional data library description
32
33 :type synopsis: str
34 :param synopsis: Optional data library synopsis
35
36 :rtype: dict
37 :return: Details of the created library.
38 For example::
39
40 {'id': 'f740ab636b360a70',
41 'name': 'Library from bioblend',
42 'url': '/api/libraries/f740ab636b360a70'}
43 """
44 payload = {'name': name}
45 if description:
46 payload['description'] = description
47 if synopsis:
48 payload['synopsis'] = synopsis
49 return self._post(payload)
50
51 def delete_library(self, library_id):
52 """
53 Delete a data library.
54
55 :type library_id: str
56 :param library_id: Encoded data library ID identifying the library to be
57 deleted
58
59 :rtype: dict
60 :return: Information about the deleted library
61
62 .. warning::
63 Deleting a data library is irreversible - all of the data from the
64 library will be permanently deleted.
65 """
66 return self._delete(id=library_id)
67
68 def _show_item(self, library_id, item_id):
69 """
70 Get details about a given library item.
71 """
72 url = '/'.join((self._make_url(library_id, contents=True), item_id))
73 return self._get(url=url)
74
75 def delete_library_dataset(self, library_id, dataset_id, purged=False):
76 """
77 Delete a library dataset in a data library.
78
79 :type library_id: str
80 :param library_id: library id where dataset is found in
81
82 :type dataset_id: str
83 :param dataset_id: id of the dataset to be deleted
84
85 :type purged: bool
86 :param purged: Indicate that the dataset should be purged (permanently
87 deleted)
88
89 :rtype: dict
90 :return: A dictionary containing the dataset id and whether the dataset
91 has been deleted.
92 For example::
93
94 {'deleted': True,
95 'id': '60e680a037f41974'}
96 """
97 url = '/'.join((self._make_url(library_id, contents=True), dataset_id))
98 return self._delete(payload={'purged': purged}, url=url)
99
100 def update_library_dataset(self, dataset_id, **kwds):
101 """
102 Update library dataset metadata. Some of the attributes that can be
103 modified are documented below.
104
105 :type dataset_id: str
106 :param dataset_id: id of the dataset to be deleted
107
108 :type name: str
109 :param name: Replace library dataset name with the given string
110
111 :type misc_info: str
112 :param misc_info: Replace library dataset misc_info with given string
113
114 :type file_ext: str
115 :param file_ext: Replace library dataset extension (must exist in the Galaxy registry)
116
117 :type genome_build: str
118 :param genome_build: Replace library dataset genome build (dbkey)
119
120 :type tags: list
121 :param tags: Replace library dataset tags with the given list
122
123 :rtype: dict
124 :return: details of the updated dataset
125 """
126 url = '/'.join((self._make_url(), 'datasets', dataset_id))
127 return self._patch(payload=kwds, url=url)
128
129 def show_dataset(self, library_id, dataset_id):
130 """
131 Get details about a given library dataset. The required ``library_id``
132 can be obtained from the datasets's library content details.
133
134 :type library_id: str
135 :param library_id: library id where dataset is found in
136
137 :type dataset_id: str
138 :param dataset_id: id of the dataset to be inspected
139
140 :rtype: dict
141 :return: A dictionary containing information about the dataset in the
142 library
143 """
144 return self._show_item(library_id, dataset_id)
145
146 def wait_for_dataset(self, library_id, dataset_id, maxwait=12000, interval=3):
147 """
148 Wait until the library dataset state is terminal ('ok', 'empty',
149 'error', 'discarded' or 'failed_metadata').
150
151 :type library_id: str
152 :param library_id: library id where dataset is found in
153
154 :type dataset_id: str
155 :param dataset_id: id of the dataset to wait for
156
157 :type maxwait: float
158 :param maxwait: Total time (in seconds) to wait for the dataset state to
159 become terminal. If the dataset state is not terminal within this
160 time, a ``DatasetTimeoutException`` will be thrown.
161
162 :type interval: float
163 :param interval: Time (in seconds) to wait between 2 consecutive checks.
164
165 :rtype: dict
166 :return: A dictionary containing information about the dataset in the
167 library
168 """
169 assert maxwait >= 0
170 assert interval > 0
171
172 time_left = maxwait
173 while True:
174 dataset = self.show_dataset(library_id, dataset_id)
175 state = dataset['state']
176 if state in TERMINAL_STATES:
177 return dataset
178 if time_left > 0:
179 log.warning("Dataset %s in library %s is in non-terminal state %s. Will wait %i more s", dataset_id, library_id, state, time_left)
180 time.sleep(min(time_left, interval))
181 time_left -= interval
182 else:
183 raise DatasetTimeoutException(f"Waited too long for dataset {dataset_id} in library {library_id} to complete")
184
185 def show_folder(self, library_id, folder_id):
186 """
187 Get details about a given folder. The required ``folder_id`` can be
188 obtained from the folder's library content details.
189
190 :type library_id: str
191 :param library_id: library id to inspect folders in
192
193 :type folder_id: str
194 :param folder_id: id of the folder to be inspected
195
196 :rtype: dict
197 :return: Information about the folder
198 """
199 return self._show_item(library_id, folder_id)
200
201 def _get_root_folder_id(self, library_id):
202 """
203 Find the root folder (i.e. '/') of a library.
204
205 :type library_id: str
206 :param library_id: library id to find root of
207 """
208 l = self.show_library(library_id=library_id)
209 return l['root_folder_id']
210
211 def create_folder(self, library_id, folder_name, description=None, base_folder_id=None):
212 """
213 Create a folder in a library.
214
215 :type library_id: str
216 :param library_id: library id to use
217
218 :type folder_name: str
219 :param folder_name: name of the new folder in the data library
220
221 :type description: str
222 :param description: description of the new folder in the data library
223
224 :type base_folder_id: str
225 :param base_folder_id: id of the folder where to create the new folder.
226 If not provided, the root folder will be used
227
228 :rtype: list
229 :return: List with a single dictionary containing information about the new folder
230 """
231 # Get root folder ID if no ID was provided
232 if base_folder_id is None:
233 base_folder_id = self._get_root_folder_id(library_id)
234 # Compose the payload
235 payload = {}
236 payload['name'] = folder_name
237 payload['folder_id'] = base_folder_id
238 payload['create_type'] = 'folder'
239 if description is not None:
240 payload['description'] = description
241 return self._post(payload, id=library_id, contents=True)
242
243 def get_folders(self, library_id, folder_id=None, name=None):
244 """
245 Get all the folders or filter specific one(s) via the provided ``name``
246 or ``folder_id`` in data library with id ``library_id``. Provide only
247 one argument: ``name`` or ``folder_id``, but not both.
248
249 :type library_id: str
250 :param library_id: library id to use
251
252 :type folder_id: str
253 :param folder_id: filter for folder by folder id
254
255 :type name: str
256 :param name: filter for folder by name. For ``name`` specify the full
257 path of the folder starting from the library's root
258 folder, e.g. ``/subfolder/subsubfolder``.
259
260 :rtype: list
261 :return: list of dicts each containing basic information about a folder
262 """
263 if folder_id is not None and name is not None:
264 raise ValueError('Provide only one argument between name or folder_id, but not both')
265 library_contents = self.show_library(library_id=library_id, contents=True)
266 if folder_id is not None:
267 folder = next((_ for _ in library_contents if _['type'] == 'folder' and _['id'] == folder_id), None)
268 folders = [folder] if folder is not None else []
269 elif name is not None:
270 folders = [_ for _ in library_contents if _['type'] == 'folder' and _['name'] == name]
271 else:
272 folders = [_ for _ in library_contents if _['type'] == 'folder']
273 return folders
274
275 def get_libraries(self, library_id=None, name=None, deleted=False):
276 """
277 Get all the libraries or filter for specific one(s) via the provided
278 name or ID. Provide only one argument: ``name`` or ``library_id``, but
279 not both.
280
281 :type library_id: str
282 :param library_id: filter for library by library id
283
284 :type name: str
285 :param name: If ``name`` is set and multiple names match the given name,
286 all the libraries matching the argument will be returned
287
288 :type deleted: bool
289 :param deleted: If ``False`` (the default), return only non-deleted
290 libraries. If ``True``, return only deleted libraries. If ``None``,
291 return both deleted and non-deleted libraries.
292
293 :rtype: list
294 :return: list of dicts each containing basic information about a library
295 """
296 if library_id is not None and name is not None:
297 raise ValueError('Provide only one argument between name or library_id, but not both')
298 libraries = self._get(params={"deleted": deleted})
299 if library_id is not None:
300 library = next((_ for _ in libraries if _['id'] == library_id), None)
301 libraries = [library] if library is not None else []
302 if name is not None:
303 libraries = [_ for _ in libraries if _['name'] == name]
304 return libraries
305
306 def show_library(self, library_id, contents=False):
307 """
308 Get information about a library.
309
310 :type library_id: str
311 :param library_id: filter for library by library id
312
313 :type contents: bool
314 :param contents: whether to get contents of the library (rather
315 than just the library details)
316
317 :rtype: dict
318 :return: details of the given library
319 """
320 return self._get(id=library_id, contents=contents)
321
322 def _do_upload(self, library_id, **keywords):
323 """
324 Set up the POST request and do the actual data upload to a data library.
325 This method should not be called directly but instead refer to the
326 methods specific for the desired type of data upload.
327 """
328 folder_id = keywords.get('folder_id', None)
329 if folder_id is None:
330 folder_id = self._get_root_folder_id(library_id)
331 files_attached = False
332 # Compose the payload dict
333 payload = {}
334 payload['folder_id'] = folder_id
335 payload['file_type'] = keywords.get('file_type', 'auto')
336 payload['dbkey'] = keywords.get('dbkey', '?')
337 payload['create_type'] = 'file'
338 if keywords.get("roles", None):
339 payload["roles"] = keywords["roles"]
340 if keywords.get("link_data_only", None) and keywords['link_data_only'] != 'copy_files':
341 payload["link_data_only"] = 'link_to_files'
342 payload['tag_using_filenames'] = keywords.get('tag_using_filenames', False)
343 if keywords.get('tags'):
344 payload['tags'] = keywords['tags']
345 payload['preserve_dirs'] = keywords.get('preserve_dirs', False)
346 # upload options
347 if keywords.get('file_url', None) is not None:
348 payload['upload_option'] = 'upload_file'
349 payload['files_0|url_paste'] = keywords['file_url']
350 elif keywords.get('pasted_content', None) is not None:
351 payload['upload_option'] = 'upload_file'
352 payload['files_0|url_paste'] = keywords['pasted_content']
353 elif keywords.get('server_dir', None) is not None:
354 payload['upload_option'] = 'upload_directory'
355 payload['server_dir'] = keywords['server_dir']
356 elif keywords.get('file_local_path', None) is not None:
357 payload['upload_option'] = 'upload_file'
358 payload['files_0|file_data'] = attach_file(keywords['file_local_path'])
359 files_attached = True
360 elif keywords.get("filesystem_paths", None) is not None:
361 payload["upload_option"] = "upload_paths"
362 payload["filesystem_paths"] = keywords["filesystem_paths"]
363
364 try:
365 return self._post(payload, id=library_id, contents=True,
366 files_attached=files_attached)
367 finally:
368 if payload.get('files_0|file_data', None) is not None:
369 payload['files_0|file_data'].close()
370
371 def upload_file_from_url(self, library_id, file_url, folder_id=None,
372 file_type='auto', dbkey='?',
373 tags=None):
374 """
375 Upload a file to a library from a URL.
376
377 :type library_id: str
378 :param library_id: id of the library where to place the uploaded file
379
380 :type file_url: str
381 :param file_url: URL of the file to upload
382
383 :type folder_id: str
384 :param folder_id: id of the folder where to place the uploaded file.
385 If not provided, the root folder will be used
386
387 :type file_type: str
388 :param file_type: Galaxy file format name
389
390 :type dbkey: str
391 :param dbkey: Dbkey
392
393 :type tags: list
394 :param tags: A list of tags to add to the datasets
395
396 :rtype: list
397 :return: List with a single dictionary containing information about the LDDA
398 """
399 return self._do_upload(library_id, file_url=file_url,
400 folder_id=folder_id, file_type=file_type,
401 dbkey=dbkey,
402 tags=tags)
403
404 def upload_file_contents(self, library_id, pasted_content,
405 folder_id=None, file_type='auto', dbkey='?',
406 tags=None):
407 """
408 Upload pasted_content to a data library as a new file.
409
410 :type library_id: str
411 :param library_id: id of the library where to place the uploaded file
412
413 :type pasted_content: str
414 :param pasted_content: Content to upload into the library
415
416 :type folder_id: str
417 :param folder_id: id of the folder where to place the uploaded file.
418 If not provided, the root folder will be used
419
420 :type file_type: str
421 :param file_type: Galaxy file format name
422
423 :type dbkey: str
424 :param dbkey: Dbkey
425
426 :type tags: list
427 :param tags: A list of tags to add to the datasets
428
429 :rtype: list
430 :return: List with a single dictionary containing information about the LDDA
431 """
432 return self._do_upload(library_id, pasted_content=pasted_content,
433 folder_id=folder_id, file_type=file_type,
434 dbkey=dbkey,
435 tags=tags)
436
437 def upload_file_from_local_path(self, library_id, file_local_path,
438 folder_id=None, file_type='auto', dbkey='?',
439 tags=None):
440 """
441 Read local file contents from file_local_path and upload data to a
442 library.
443
444 :type library_id: str
445 :param library_id: id of the library where to place the uploaded file
446
447 :type file_local_path: str
448 :param file_local_path: path of local file to upload
449
450 :type folder_id: str
451 :param folder_id: id of the folder where to place the uploaded file.
452 If not provided, the root folder will be used
453
454 :type file_type: str
455 :param file_type: Galaxy file format name
456
457 :type dbkey: str
458 :param dbkey: Dbkey
459
460 :type tags: list
461 :param tags: A list of tags to add to the datasets
462
463 :rtype: list
464 :return: List with a single dictionary containing information about the LDDA
465 """
466 return self._do_upload(library_id, file_local_path=file_local_path,
467 folder_id=folder_id, file_type=file_type,
468 dbkey=dbkey,
469 tags=tags)
470
471 def upload_file_from_server(self, library_id, server_dir, folder_id=None,
472 file_type='auto', dbkey='?', link_data_only=None,
473 roles="", preserve_dirs=False, tag_using_filenames=False,
474 tags=None):
475 """
476 Upload all files in the specified subdirectory of the Galaxy library
477 import directory to a library.
478
479 .. note::
480 For this method to work, the Galaxy instance must have the
481 ``library_import_dir`` option configured in the ``config/galaxy.yml``
482 configuration file.
483
484 :type library_id: str
485 :param library_id: id of the library where to place the uploaded file
486
487 :type server_dir: str
488 :param server_dir: relative path of the subdirectory of
489 ``library_import_dir`` to upload. All and only the files (i.e. no
490 subdirectories) contained in the specified directory will be
491 uploaded
492
493 :type folder_id: str
494 :param folder_id: id of the folder where to place the uploaded files.
495 If not provided, the root folder will be used
496
497 :type file_type: str
498 :param file_type: Galaxy file format name
499
500 :type dbkey: str
501 :param dbkey: Dbkey
502
503 :type link_data_only: str
504 :param link_data_only: either 'copy_files' (default) or
505 'link_to_files'. Setting to 'link_to_files' symlinks instead of
506 copying the files
507
508 :type roles: str
509 :param roles: ???
510
511 :type preserve_dirs: bool
512 :param preserve_dirs: Indicate whether to preserve the directory structure when importing dir
513
514 :type tag_using_filenames: bool
515 :param tag_using_filenames: Indicate whether to generate dataset tags
516 from filenames.
517
518 .. versionchanged:: 0.14.0
519 Changed the default from ``True`` to ``False``.
520
521 :type tags: list
522 :param tags: A list of tags to add to the datasets
523
524 :rtype: list
525 :return: List with a single dictionary containing information about the LDDA
526 """
527 return self._do_upload(library_id, server_dir=server_dir,
528 folder_id=folder_id, file_type=file_type,
529 dbkey=dbkey, link_data_only=link_data_only,
530 roles=roles, preserve_dirs=preserve_dirs,
531 tag_using_filenames=tag_using_filenames,
532 tags=tags)
533
534 def upload_from_galaxy_filesystem(self, library_id, filesystem_paths, folder_id=None,
535 file_type="auto", dbkey="?", link_data_only=None,
536 roles="", preserve_dirs=False, tag_using_filenames=False,
537 tags=None):
538 """
539 Upload a set of files already present on the filesystem of the Galaxy
540 server to a library.
541
542 .. note::
543 For this method to work, the Galaxy instance must have the
544 ``allow_path_paste`` option set to ``true`` in the
545 ``config/galaxy.yml`` configuration file.
546
547 :type library_id: str
548 :param library_id: id of the library where to place the uploaded file
549
550 :type filesystem_paths: str
551 :param filesystem_paths: file paths on the Galaxy server to upload to
552 the library, one file per line
553
554 :type folder_id: str
555 :param folder_id: id of the folder where to place the uploaded files.
556 If not provided, the root folder will be used
557
558 :type file_type: str
559 :param file_type: Galaxy file format name
560
561 :type dbkey: str
562 :param dbkey: Dbkey
563
564 :type link_data_only: str
565 :param link_data_only: either 'copy_files' (default) or
566 'link_to_files'. Setting to 'link_to_files' symlinks instead of
567 copying the files
568
569 :type roles: str
570 :param roles: ???
571
572 :type preserve_dirs: bool
573 :param preserve_dirs: Indicate whether to preserve the directory structure when importing dir
574
575 :type tag_using_filenames: bool
576 :param tag_using_filenames: Indicate whether to generate dataset tags
577 from filenames.
578
579 .. versionchanged:: 0.14.0
580 Changed the default from ``True`` to ``False``.
581
582 :type tags: list
583 :param tags: A list of tags to add to the datasets
584
585 :rtype: list
586 :return: List with a single dictionary containing information about the LDDA
587 """
588 return self._do_upload(library_id, filesystem_paths=filesystem_paths,
589 folder_id=folder_id, file_type=file_type,
590 dbkey=dbkey, link_data_only=link_data_only,
591 roles=roles, preserve_dirs=preserve_dirs,
592 tag_using_filenames=tag_using_filenames,
593 tags=tags)
594
595 def copy_from_dataset(self, library_id, dataset_id, folder_id=None, message=''):
596 """
597 Copy a Galaxy dataset into a library.
598
599 :type library_id: str
600 :param library_id: id of the library where to place the uploaded file
601
602 :type dataset_id: str
603 :param dataset_id: id of the dataset to copy from
604
605 :type folder_id: str
606 :param folder_id: id of the folder where to place the uploaded files.
607 If not provided, the root folder will be used
608
609 :type message: str
610 :param message: message for copying action
611
612 :rtype: dict
613 :return: LDDA information
614 """
615 if folder_id is None:
616 folder_id = self._get_root_folder_id(library_id)
617 payload = {}
618 payload['folder_id'] = folder_id
619 payload['create_type'] = 'file'
620 payload['from_hda_id'] = dataset_id
621 payload['ldda_message'] = message
622 return self._post(payload, id=library_id, contents=True)
623
624 def get_library_permissions(self, library_id):
625 """
626 Get the permissions for a library.
627
628 :type library_id: str
629 :param library_id: id of the library
630
631 :rtype: dict
632 :return: dictionary with all applicable permissions' values
633 """
634 url = self._make_url(library_id) + '/permissions'
635 return self._get(url=url)
636
637 def get_dataset_permissions(self, dataset_id):
638 """
639 Get the permissions for a dataset.
640
641 :type dataset_id: str
642 :param dataset_id: id of the dataset
643
644 :rtype: dict
645 :return: dictionary with all applicable permissions' values
646 """
647 url = '/'.join((self._make_url(), 'datasets', dataset_id, 'permissions'))
648 return self._get(url=url)
649
650 def set_library_permissions(self, library_id, access_in=None,
651 modify_in=None, add_in=None, manage_in=None):
652 """
653 Set the permissions for a library. Note: it will override all security
654 for this library even if you leave out a permission type.
655
656 :type library_id: str
657 :param library_id: id of the library
658
659 :type access_in: list
660 :param access_in: list of role ids
661
662 :type modify_in: list
663 :param modify_in: list of role ids
664
665 :type add_in: list
666 :param add_in: list of role ids
667
668 :type manage_in: list
669 :param manage_in: list of role ids
670
671 :rtype: dict
672 :return: General information about the library
673 """
674 payload = {}
675 if access_in:
676 payload['LIBRARY_ACCESS_in'] = access_in
677 if modify_in:
678 payload['LIBRARY_MODIFY_in'] = modify_in
679 if add_in:
680 payload['LIBRARY_ADD_in'] = add_in
681 if manage_in:
682 payload['LIBRARY_MANAGE_in'] = manage_in
683 url = self._make_url(library_id) + '/permissions'
684 return self._post(payload, url=url)
685
686 def set_dataset_permissions(self, dataset_id, access_in=None,
687 modify_in=None, manage_in=None):
688 """
689 Set the permissions for a dataset. Note: it will override all security
690 for this dataset even if you leave out a permission type.
691
692 :type dataset_id: str
693 :param dataset_id: id of the dataset
694
695 :type access_in: list
696 :param access_in: list of role ids
697
698 :type modify_in: list
699 :param modify_in: list of role ids
700
701 :type manage_in: list
702 :param manage_in: list of role ids
703
704 :rtype: dict
705 :return: dictionary with all applicable permissions' values
706 """
707 payload = {}
708 if access_in:
709 payload['access_ids[]'] = access_in
710 if modify_in:
711 payload['modify_ids[]'] = modify_in
712 if manage_in:
713 payload['manage_ids[]'] = manage_in
714 # we need here to define an action
715 payload['action'] = 'set_permissions'
716 url = '/'.join((self._make_url(), 'datasets', dataset_id, 'permissions'))
717 return self._post(payload, url=url)