ibridgesgui 0.2.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.
Files changed (54) hide show
  1. ibridgesgui/__init__.py +1 -0
  2. ibridgesgui/__main__.py +164 -0
  3. ibridgesgui/_version.py +16 -0
  4. ibridgesgui/browser.py +625 -0
  5. ibridgesgui/config.py +250 -0
  6. ibridgesgui/gui_utils.py +118 -0
  7. ibridgesgui/icons/arrow-left.png +0 -0
  8. ibridgesgui/icons/arrow-right.png +0 -0
  9. ibridgesgui/icons/arrow-up.png +0 -0
  10. ibridgesgui/icons/home.png +0 -0
  11. ibridgesgui/icons/iBridges.ico +0 -0
  12. ibridgesgui/icons/loading_circle.gif +0 -0
  13. ibridgesgui/icons/nosync.png +0 -0
  14. ibridgesgui/icons/refresh.png +0 -0
  15. ibridgesgui/icons/syncing.png +0 -0
  16. ibridgesgui/info.py +53 -0
  17. ibridgesgui/irods_tree_model.py +158 -0
  18. ibridgesgui/login.py +101 -0
  19. ibridgesgui/popup_widgets.py +237 -0
  20. ibridgesgui/search.py +285 -0
  21. ibridgesgui/sync.py +289 -0
  22. ibridgesgui/threads.py +251 -0
  23. ibridgesgui/ui_files/.py +118 -0
  24. ibridgesgui/ui_files/ExampleTab.py +87 -0
  25. ibridgesgui/ui_files/ExampleTab.ui +93 -0
  26. ibridgesgui/ui_files/MainMenu.py +121 -0
  27. ibridgesgui/ui_files/MainMenu.ui +171 -0
  28. ibridgesgui/ui_files/__init__.py +1 -0
  29. ibridgesgui/ui_files/configCheck.py +96 -0
  30. ibridgesgui/ui_files/configCheck.ui +142 -0
  31. ibridgesgui/ui_files/createCollection.py +82 -0
  32. ibridgesgui/ui_files/createCollection.ui +153 -0
  33. ibridgesgui/ui_files/dataTransferState.py +114 -0
  34. ibridgesgui/ui_files/dataTransferState.ui +187 -0
  35. ibridgesgui/ui_files/irodsLogin.py +134 -0
  36. ibridgesgui/ui_files/irodsLogin.ui +237 -0
  37. ibridgesgui/ui_files/renameItem.py +77 -0
  38. ibridgesgui/ui_files/renameItem.ui +151 -0
  39. ibridgesgui/ui_files/tabBrowser.py +525 -0
  40. ibridgesgui/ui_files/tabBrowser.ui +829 -0
  41. ibridgesgui/ui_files/tabInfo.py +207 -0
  42. ibridgesgui/ui_files/tabInfo.ui +356 -0
  43. ibridgesgui/ui_files/tabSearch.py +233 -0
  44. ibridgesgui/ui_files/tabSearch.ui +400 -0
  45. ibridgesgui/ui_files/tabSync.py +223 -0
  46. ibridgesgui/ui_files/tabSync.ui +481 -0
  47. ibridgesgui/ui_files/tabUpDownload.py +303 -0
  48. ibridgesgui/ui_files/tabUpDownload.ui +460 -0
  49. ibridgesgui-0.2.0.dist-info/LICENSE +674 -0
  50. ibridgesgui-0.2.0.dist-info/METADATA +787 -0
  51. ibridgesgui-0.2.0.dist-info/RECORD +54 -0
  52. ibridgesgui-0.2.0.dist-info/WHEEL +5 -0
  53. ibridgesgui-0.2.0.dist-info/entry_points.txt +2 -0
  54. ibridgesgui-0.2.0.dist-info/top_level.txt +1 -0
ibridgesgui/browser.py ADDED
@@ -0,0 +1,625 @@
1
+ """Browser tab."""
2
+
3
+ import logging
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import irods.exception
8
+ import PyQt6.QtCore
9
+ import PyQt6.QtGui
10
+ import PyQt6.QtWidgets
11
+ import PyQt6.uic
12
+ from ibridges import IrodsPath, download, upload
13
+ from ibridges.meta import MetaData
14
+ from ibridges.permissions import Permissions
15
+ from ibridges.util import obj_replicas
16
+
17
+ from ibridgesgui.gui_utils import (
18
+ UI_FILE_DIR,
19
+ get_coll_dict,
20
+ get_downloads_dir,
21
+ get_irods_item,
22
+ populate_table,
23
+ populate_textfield,
24
+ )
25
+ from ibridgesgui.popup_widgets import CreateCollection, Rename
26
+ from ibridgesgui.ui_files.tabBrowser import Ui_tabBrowser
27
+
28
+
29
+ class Browser(PyQt6.QtWidgets.QWidget, Ui_tabBrowser):
30
+ """Browser view for iRODS session."""
31
+
32
+ def __init__(self, session, app_name):
33
+ """Initialize an iRODS browser view."""
34
+ super().__init__()
35
+ if getattr(sys, "frozen", False):
36
+ super().setupUi(self)
37
+ else:
38
+ PyQt6.uic.loadUi(UI_FILE_DIR / "tabBrowser.ui", self)
39
+
40
+ self.logger = logging.getLogger(app_name)
41
+ self.session = session
42
+ self.info_tabs.setCurrentIndex(0)
43
+ # iRODS default home
44
+ if self.session.home is not None:
45
+ root_path = IrodsPath(self.session).absolute()
46
+ else:
47
+ root_path = IrodsPath(
48
+ self.session, f"/{self.session.zone}/home/{self.session.username}"
49
+ )
50
+
51
+ if root_path.collection_exists():
52
+ self.root_coll = IrodsPath(self.session, root_path).collection
53
+ elif IrodsPath(self.session, f"/{self.session.zone}/home").collection_exists():
54
+ self.root_coll = IrodsPath(self.session, f"/{self.session.zone}/home").collection
55
+ else:
56
+ self.error_label.setText(
57
+ 'Cannot set root collection. Set "irods_home" in your environment.json'
58
+ )
59
+ self.reset_path()
60
+ self.browse()
61
+
62
+ def browse(self):
63
+ """Initialize browser view GUI elements. Define the signals and slots."""
64
+ # Main navigation elements
65
+ self.path_input.returnPressed.connect(self.load_browser_table)
66
+ self.refresh_button.clicked.connect(self.load_browser_table)
67
+ self.refresh_button.setToolTip("Refresh")
68
+ self.home_button.clicked.connect(self.reset_path)
69
+ self.home_button.setToolTip("Home")
70
+ self.parent_button.clicked.connect(self.set_parent)
71
+ self.parent_button.setToolTip("Parent Coll")
72
+
73
+ # Main manipulation buttons Upload/Download create collection
74
+ self.upload_file_button.clicked.connect(self.file_upload)
75
+ self.upload_dir_button.clicked.connect(self.folder_upload)
76
+ self.download_button.clicked.connect(self.download)
77
+ self.create_coll_button.clicked.connect(self.create_collection)
78
+ self.rename_button.clicked.connect(self.rename_item)
79
+
80
+ # Browser table behaviour
81
+ self.browser_table.doubleClicked.connect(self.update_path)
82
+ self.browser_table.clicked.connect(self.fill_info)
83
+
84
+ # Bottom tab view buttons
85
+ # Metadata
86
+ self.meta_table.clicked.connect(self.edit_metadata)
87
+ self.add_meta_button.clicked.connect(self.add_icat_meta)
88
+ self.update_meta_button.clicked.connect(self.set_icat_meta)
89
+ self.delete_meta_button.clicked.connect(self.delete_icat_meta)
90
+ # ACLs
91
+ self.acl_table.clicked.connect(self.edit_acl)
92
+ self.add_acl_button.clicked.connect(self.update_icat_acl)
93
+ # Delete
94
+ self.confirm_button.clicked.connect(self.delete_data)
95
+ self.load_selection_button.clicked.connect(self.load_selection)
96
+
97
+ def reset_path(self):
98
+ """Reset browser table to root path."""
99
+ self.path_input.setText(self.root_coll.path)
100
+ self.load_browser_table()
101
+
102
+ def set_parent(self):
103
+ """Set browser path to parent of current collection and update browser table."""
104
+ current_path = IrodsPath(self.session, self.path_input.text())
105
+ self.path_input.setText(str(current_path.parent))
106
+ self.load_browser_table()
107
+
108
+ def update_path(self, index):
109
+ """Take path from path_input and loads browser table."""
110
+ self.error_label.clear()
111
+ row = index.row()
112
+ irods_path = self._get_item_path(row)
113
+ if irods_path.collection_exists():
114
+ self.path_input.setText(str(irods_path))
115
+ self.load_browser_table()
116
+
117
+ def create_collection(self):
118
+ """Create a new collection in current collection."""
119
+ self.error_label.clear()
120
+ parent = IrodsPath(self.session, "/" + self.path_input.text().strip("/"))
121
+ coll_widget = CreateCollection(parent, self.logger)
122
+ coll_widget.exec()
123
+ self.load_browser_table()
124
+
125
+ def rename_item(self):
126
+ """Rename/move a collection or data object."""
127
+ self.error_label.clear()
128
+ if self.browser_table.currentRow() == -1:
129
+ self.error_label.setText("Please select a row from the table first!")
130
+ return
131
+ item_name = self.browser_table.item(self.browser_table.currentRow(), 1).text()
132
+ irods_path = IrodsPath(self.session, "/" + self.path_input.text().strip("/")).joinpath(
133
+ item_name
134
+ )
135
+ rename_widget = Rename(irods_path, self.logger)
136
+ rename_widget.exec()
137
+ self.load_browser_table()
138
+
139
+ def folder_upload(self):
140
+ """Select a folder and upload."""
141
+ self.error_label.clear()
142
+ select_dir = PyQt6.QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory")
143
+ path = self._fs_select(select_dir)
144
+ if path is not None:
145
+ self._upload(path)
146
+
147
+ def file_upload(self):
148
+ """Select a file and upload."""
149
+ self.error_label.clear()
150
+ select_file = PyQt6.QtWidgets.QFileDialog.getOpenFileName(self, "Open Filie")
151
+ path = self._fs_select(select_file)
152
+ if path is not None:
153
+ self._upload(path)
154
+
155
+ def download(self):
156
+ """Download collection or data object."""
157
+ self.error_label.clear()
158
+ if self.browser_table.currentRow() == -1:
159
+ self.error_label.setText("Please select a row from the table first!")
160
+ return
161
+
162
+ if self.browser_table.item(self.browser_table.currentRow(), 1) is not None:
163
+ item_name = self.browser_table.item(self.browser_table.currentRow(), 1).text()
164
+ path = IrodsPath(self.session, "/", *self.path_input.text().split("/"), item_name)
165
+ overwrite = self.overwrite.isChecked()
166
+ download_dir = get_downloads_dir()
167
+ if overwrite:
168
+ write = "All data will be updated."
169
+ else:
170
+ write = "Only new data will be added."
171
+ info = f"Download data:\n{path}\n\nto\n\n{download_dir}\n\n{write}"
172
+
173
+ try:
174
+ if path.exists():
175
+ button_reply = PyQt6.QtWidgets.QMessageBox.question(self, "", info)
176
+ if button_reply == PyQt6.QtWidgets.QMessageBox.StandardButton.Yes:
177
+ if Path(download_dir).joinpath(item_name).exists() and not overwrite:
178
+ raise FileExistsError
179
+ self.setCursor(PyQt6.QtGui.QCursor(PyQt6.QtCore.Qt.CursorShape.BusyCursor))
180
+ self.logger.info(
181
+ "Downloading %s to %s, overwrite %s", path, download_dir, str(overwrite)
182
+ )
183
+ download(self.session, path, download_dir, overwrite=overwrite)
184
+ self.setCursor(PyQt6.QtGui.QCursor(PyQt6.QtCore.Qt.CursorShape.ArrowCursor))
185
+ self.error_label.setText("Data downloaded to: " + str(download_dir))
186
+ else:
187
+ self.error_label.setText(f"Data {path.parent} does not exist.")
188
+ except FileExistsError:
189
+ self.error_label.setText(
190
+ f"Data already exists in {download_dir}."
191
+ + ' Check "overwrite" to overwrite the data.'
192
+ )
193
+ except Exception as err:
194
+ self.logger.exception("Downloading %s failed: %s", path, err)
195
+ self.error_label.setText(f"Could not download {path}. Consult the logs.")
196
+
197
+ def load_browser_table(self):
198
+ """Load main browser table."""
199
+ self.error_label.clear()
200
+ self._clear_info_tabs()
201
+ obj_path = IrodsPath(self.session, self.path_input.text())
202
+ if obj_path.collection_exists():
203
+ try:
204
+ coll = obj_path.collection
205
+
206
+ coll_data = [
207
+ (
208
+ "C-",
209
+ subcoll.name,
210
+ "",
211
+ "",
212
+ subcoll.create_time.strftime("%d-%m-%Y"),
213
+ subcoll.modify_time.strftime("%d-%m-%Y %H:%m"),
214
+ )
215
+ for subcoll in coll.subcollections
216
+ ]
217
+ obj_data = [
218
+ (
219
+ max(repl[4] for repl in obj_replicas(obj)),
220
+ obj.name,
221
+ str(obj.size),
222
+ obj.checksum,
223
+ obj.create_time.strftime("%d-%m-%Y"),
224
+ obj.modify_time.strftime("%d-%m-%Y %H:%m"),
225
+ )
226
+ for obj in coll.data_objects
227
+ ]
228
+
229
+ populate_table(
230
+ self.browser_table,
231
+ len(coll.data_objects) + len(coll.subcollections),
232
+ coll_data + obj_data,
233
+ )
234
+ self.browser_table.resizeColumnsToContents()
235
+ except Exception:
236
+ self.browser_table.setRowCount(0)
237
+ self.logger.exception("Cannot load browser.")
238
+ self.error_label.setText("Cannot load browser table. Consult the logs.")
239
+ else:
240
+ self.browser_table.setRowCount(0)
241
+ self.error_label.setText("Collection does not exist.")
242
+
243
+ def fill_info(self):
244
+ """Fill lower tabs with info."""
245
+ self.error_label.clear()
246
+ self._clear_info_tabs()
247
+ self.delete_browser.clear()
248
+ self.meta_table.setRowCount(0)
249
+ self.acl_table.setRowCount(0)
250
+ self.replica_table.setRowCount(0)
251
+ irods_path = self._get_item_path(self.browser_table.currentRow())
252
+ self._clear_info_tabs()
253
+ try:
254
+ self._fill_preview_tab(irods_path)
255
+ self._fill_metadata_tab(irods_path)
256
+ self._fill_acls_tab(irods_path)
257
+ self._fill_replicas_tab(irods_path)
258
+ except Exception:
259
+ self.logger.exception("Cannot load info tabs.")
260
+ self.error_label.setText("Cannot load info tabs. Consult the logs.")
261
+
262
+ def set_icat_meta(self):
263
+ """Button metadata set."""
264
+ try:
265
+ self._metadata_edits("set")
266
+ except Exception as error:
267
+ self.error_label.setText(repr(error))
268
+
269
+ def add_icat_meta(self):
270
+ """Button metadata add."""
271
+ try:
272
+ self._metadata_edits("add")
273
+ except Exception as error:
274
+ self.error_label.setText(repr(error))
275
+
276
+ def delete_icat_meta(self):
277
+ """Button metadata delete."""
278
+ try:
279
+ self._metadata_edits("delete")
280
+ except Exception as error:
281
+ self.error_label.setText(repr(error))
282
+
283
+ # @PyQt6.QtCore.pyqtSlot(PyQt6.QtCore.QModelIndex)
284
+ def edit_metadata(self, index):
285
+ """Load selected metadata into edit fields."""
286
+ self.error_label.clear()
287
+ self.meta_key_field.clear()
288
+ self.meta_value_field.clear()
289
+ self.meta_units_field.clear()
290
+ row = index.row()
291
+ key = self.meta_table.item(row, 0).text()
292
+ value = self.meta_table.item(row, 1).text()
293
+ if self.meta_table.item(row, 2):
294
+ units = self.meta_table.item(row, 2).text()
295
+ else:
296
+ units = ""
297
+ self.meta_key_field.setText(key)
298
+ self.meta_value_field.setText(value)
299
+ self.meta_units_field.setText(units)
300
+
301
+ # @PyQt6.QtCore.pyqtSlot(PyQt6.QtCore.QModelIndex)
302
+ def edit_acl(self, index):
303
+ """Load selected acl into editing fields."""
304
+ self.error_label.clear()
305
+ self.acl_user_field.clear()
306
+ self.acl_zone_field.clear()
307
+ self.acl_box.setCurrentText("")
308
+ row = index.row()
309
+ user_name = self.acl_table.item(row, 0).text()
310
+ user_zone = self.acl_table.item(row, 1).text()
311
+ acc_name = self.acl_table.item(row, 2).text()
312
+ self.acl_user_field.setText(user_name)
313
+ self.acl_zone_field.setText(user_zone)
314
+ self.acl_box.setCurrentText(acc_name)
315
+
316
+ def update_icat_acl(self):
317
+ """Send acls to iRODS server."""
318
+ self.error_label.clear()
319
+ if self.browser_table.currentRow() == -1:
320
+ self.error_label.setText("Please select a row from the table first!")
321
+ return
322
+ irods_path = self._get_item_path(self.browser_table.currentRow())
323
+ user_name = self.acl_user_field.text()
324
+ user_zone = self.acl_zone_field.text()
325
+ acc_name = self.acl_box.currentText()
326
+
327
+ if acc_name in ("inherit", "noinherit"):
328
+ if irods_path.dataobject_exists():
329
+ self.error_label.setText("WARNING: (no)inherit is not applicable to data objects")
330
+ return
331
+ elif user_name == "":
332
+ self.error_label.setText("Please provide a user.")
333
+ return
334
+ elif acc_name == "":
335
+ self.error_label.setText("Please provide an access level from the menu.")
336
+ return
337
+ recursive = self.recurisive_box.currentText() == "True"
338
+ try:
339
+ item = get_irods_item(irods_path)
340
+ perm = Permissions(self.session, item)
341
+ perm.set(perm=acc_name, user=user_name, zone=user_zone, recursive=recursive)
342
+ if acc_name == "null":
343
+ self.logger.info(
344
+ "Delete access (%s, %s, %s, %s) for %s",
345
+ acc_name,
346
+ user_name,
347
+ user_zone,
348
+ str(recursive),
349
+ item.path,
350
+ )
351
+ else:
352
+ self.logger.info(
353
+ "Add/change access of %s to (%s, %s, %s, %s)",
354
+ item.path,
355
+ acc_name,
356
+ user_name,
357
+ user_zone,
358
+ str(recursive),
359
+ )
360
+ self._fill_acls_tab(irods_path)
361
+ except irods.exception.CAT_INVALID_USER:
362
+ self.error_label.setText(f"Cannot update ACLs. {user_name}#{user_zone} not known.")
363
+ except Exception:
364
+ self.logger.exception("Cannot update ACLs.")
365
+ self.error_label.setText("Cannot update ACLs. Consult the logs.")
366
+
367
+ def load_selection(self):
368
+ """Load selection from main table into delete tab."""
369
+ self.setCursor(PyQt6.QtGui.QCursor(PyQt6.QtCore.Qt.CursorShape.WaitCursor))
370
+ self.delete_browser.clear()
371
+ row = self.browser_table.currentRow()
372
+ if row == -1:
373
+ self.error_label.setText("Please select a row from the table first.")
374
+ self.setCursor(PyQt6.QtGui.QCursor(PyQt6.QtCore.Qt.CursorShape.ArrowCursor))
375
+ return
376
+ content = []
377
+ item_path = self._get_item_path(row)
378
+ if item_path.exists():
379
+ item = get_irods_item(item_path)
380
+ if item_path.collection_exists():
381
+ data_dict = get_coll_dict(item)
382
+ for key in list(data_dict.keys())[:20]:
383
+ content.append(key)
384
+ if len(data_dict[key]) > 0:
385
+ for item in data_dict[key]:
386
+ content.append("\t" + item)
387
+ content.append("...")
388
+ else:
389
+ content.append(str(item_path))
390
+ populate_textfield(self.delete_browser, content)
391
+ self.setCursor(PyQt6.QtGui.QCursor(PyQt6.QtCore.Qt.CursorShape.ArrowCursor))
392
+
393
+ def delete_data(self):
394
+ """Delete all data in the delete_browser."""
395
+ self.error_label.clear()
396
+ data = self.delete_browser.toPlainText().split("\n")
397
+ if data[0] != "":
398
+ item = data[0].strip()
399
+ quit_msg = "Delete all data in \n\n" + item + "\n"
400
+ reply = PyQt6.QtWidgets.QMessageBox.question(
401
+ self,
402
+ "Message",
403
+ quit_msg,
404
+ PyQt6.QtWidgets.QMessageBox.StandardButton.Yes,
405
+ PyQt6.QtWidgets.QMessageBox.StandardButton.No,
406
+ )
407
+ if reply == PyQt6.QtWidgets.QMessageBox.StandardButton.Yes:
408
+ try:
409
+ IrodsPath(self.session, item).remove()
410
+ self.logger.info("Delete data %s", item)
411
+ self.delete_browser.clear()
412
+ self.load_browser_table()
413
+ self.error_label.clear()
414
+ except (irods.exception.CAT_NO_ACCESS_PERMISSION, PermissionError):
415
+ self.error_label.setText(f"No permissions to delete {item}")
416
+ except Exception:
417
+ self.logger.exception("FAILED: Delete data %s", item)
418
+ self.error_label.setText(f"FAILED: Delete data {item}. Consult the logs.")
419
+
420
+ # Internal functions
421
+ def _clear_info_tabs(self):
422
+ """Clear the tabs view."""
423
+ self.acl_table.setRowCount(0)
424
+ self.meta_table.setRowCount(0)
425
+ self.replica_table.setRowCount(0)
426
+ self.preview_browser.clear()
427
+
428
+ def _fill_replicas_tab(self, irods_path):
429
+ """Populate the table in the Replicas tab.
430
+
431
+ Parameters
432
+ ----------
433
+ irods_path : str
434
+ Path of iRODS collection or data object selected.
435
+
436
+ """
437
+ self.replica_table.setRowCount(0)
438
+ if irods_path.dataobject_exists():
439
+ obj = irods_path.dataobject
440
+ populate_table(self.replica_table, len(obj_replicas(obj)), obj_replicas(obj))
441
+ self.replica_table.setRowCount(len(obj.replicas))
442
+ self.replica_table.resizeColumnsToContents()
443
+
444
+ def _fill_acls_tab(self, irods_path):
445
+ """Populate the table in the ACLs tab.
446
+
447
+ Parameters
448
+ ----------
449
+ irods_path : str
450
+ Path of iRODS collection or data object selected.
451
+
452
+ """
453
+ self.acl_table.setRowCount(0)
454
+ self.acl_user_field.clear()
455
+ self.acl_zone_field.clear()
456
+ self.acl_box.setCurrentText("")
457
+ obj = None
458
+ if irods_path.collection_exists():
459
+ obj = irods_path.collection
460
+ inheritance = f"{obj.inheritance}"
461
+ elif irods_path.dataobject_exists():
462
+ obj = irods_path.dataobject
463
+ inheritance = ""
464
+ if obj is not None:
465
+ acls = Permissions(self.session, obj)
466
+ acl_data = [(p.user_name, p.user_zone, p.access_name, inheritance) for p in acls]
467
+ populate_table(self.acl_table, len(list(acls)), acl_data)
468
+ self.acl_table.resizeColumnsToContents()
469
+ self.owner_label.setText(f"{obj.owner_name}")
470
+
471
+ def _fill_metadata_tab(self, irods_path):
472
+ """Populate the table in the metadata tab.
473
+
474
+ Parameters
475
+ ----------
476
+ irods_path : str
477
+ Full name of iRODS collection or data object selected.
478
+
479
+ """
480
+ self.meta_key_field.clear()
481
+ self.meta_value_field.clear()
482
+ self.meta_units_field.clear()
483
+ item = None
484
+ if irods_path.collection_exists():
485
+ item = irods_path.collection
486
+ elif irods_path.dataobject_exists():
487
+ item = irods_path.dataobject
488
+ if item is not None:
489
+ meta = MetaData(item)
490
+ populate_table(self.meta_table, len(list(meta)), meta)
491
+ self.meta_table.resizeColumnsToContents()
492
+
493
+ def _fill_preview_tab(self, irods_path):
494
+ """Populate the table in the metadata tab.
495
+
496
+ Parameters
497
+ ----------
498
+ irods_path : str
499
+ Full name of iRODS collection or data object selected.
500
+
501
+ """
502
+ if irods_path.collection_exists():
503
+ obj = irods_path.collection
504
+ content = ["Collections:", "-----------------"]
505
+ content.extend([sc.name for sc in obj.subcollections])
506
+ content.extend(["\n", "DataObjects:", "-----------------"])
507
+ content.extend([do.name for do in obj.data_objects])
508
+ elif irods_path.dataobject_exists():
509
+ file_type = ""
510
+ obj = irods_path.dataobject
511
+ if "." in irods_path.parts[-1]:
512
+ file_type = irods_path.parts[-1].split(".")[1]
513
+ if file_type in ["txt", "json", "csv"]:
514
+ try:
515
+ with obj.open("r") as objfd:
516
+ content = [objfd.read(1024).decode("utf-8")]
517
+ # self.preview_browser.append(preview_string)
518
+ except Exception as error:
519
+ content = [
520
+ f"No Preview for: {irods_path}",
521
+ repr(error),
522
+ "Storage resource might be down.",
523
+ ]
524
+ else:
525
+ content = [f"No Preview for: {irods_path}"]
526
+ populate_textfield(self.preview_browser, content)
527
+
528
+ def _get_item_path(self, row):
529
+ item_name = self.browser_table.item(row, 1).text()
530
+ return IrodsPath(self.session, "/", *self.path_input.text().split("/"), item_name)
531
+
532
+ def _metadata_edits(self, operation):
533
+ self.error_label.clear()
534
+ if self.browser_table.currentRow() == -1:
535
+ self.error_label.setText("Please select an object first!")
536
+ else:
537
+ irods_path = self._get_item_path(self.browser_table.currentRow())
538
+ new_key = self.meta_key_field.text()
539
+ new_val = self.meta_value_field.text()
540
+ new_units = self.meta_units_field.text()
541
+ if new_key != "" and new_val != "":
542
+ irods_path = self._get_item_path(self.browser_table.currentRow())
543
+ item = get_irods_item(irods_path)
544
+ meta = MetaData(item)
545
+ if operation == "add":
546
+ meta.add(new_key, new_val, new_units)
547
+ self.logger.info(
548
+ "Add metadata (%s, %s, %s) to %s", new_key, new_val, new_units, irods_path
549
+ )
550
+ elif operation == "set":
551
+ meta.set(new_key, new_val, new_units)
552
+ self.logger.info(
553
+ "Set all metadata with key %s to (%s, %s, %s) for %s",
554
+ new_key,
555
+ new_key,
556
+ new_val,
557
+ new_units,
558
+ irods_path,
559
+ )
560
+ elif operation == "delete":
561
+ meta.delete(new_key, new_val, new_units)
562
+ self.logger.info(
563
+ "Delete metadata (%s, %s, %s) from %s",
564
+ new_key,
565
+ new_val,
566
+ new_units,
567
+ irods_path,
568
+ )
569
+ self._fill_metadata_tab(irods_path)
570
+
571
+ def _fs_select(self, path_select):
572
+ """Retrieve the path (file or folder) from a QFileDialog.
573
+
574
+ Parameters
575
+ ----------
576
+ path_select: PyQt6.QtWidgets.QFileDialog.getExistingDirectory
577
+ PyQt6.QtWidgets.QFileDialog.getOpenFileName
578
+
579
+ """
580
+ yes_button = PyQt6.QtWidgets.QMessageBox.StandardButton.Yes
581
+ if isinstance(path_select, tuple):
582
+ path = path_select[0]
583
+ else:
584
+ path = path_select
585
+
586
+ if path != "":
587
+ if self.overwrite.isChecked():
588
+ write = "All data will be updated."
589
+ else:
590
+ write = "Only new data will be added."
591
+ info = f"Upload data:\n{path}\n\nto\n{self.path_input.text()}\n\n{write}"
592
+ reply = PyQt6.QtWidgets.QMessageBox.question(self, "", info)
593
+ if reply == yes_button:
594
+ return Path(path)
595
+ return None
596
+
597
+ def _upload(self, source):
598
+ """Upload data to path in path_input."""
599
+ overwrite = self.overwrite.isChecked()
600
+ parent_path = IrodsPath(self.session, "/", *self.path_input.text().split("/"))
601
+
602
+ try:
603
+ if parent_path.joinpath(source.name).exists() and not overwrite:
604
+ raise FileExistsError
605
+ self.logger.info(
606
+ "Uploading %s to %s, overwrite %s", source, parent_path, str(overwrite)
607
+ )
608
+ upload(self.session, source, parent_path, overwrite=overwrite)
609
+ self.load_browser_table()
610
+ except FileExistsError:
611
+ self.error_label.setText(
612
+ f"Data already exists in {parent_path}."
613
+ + ' Check "overwrite" to overwrite the data.'
614
+ )
615
+ except irods.exception.CAT_NO_ACCESS_PERMISSION:
616
+ self.error_label.setText(f"No permission to upload data to {parent_path}.")
617
+ self.logger.info(
618
+ "Uploading %s to %s, overwrite %s failed. No permissions.",
619
+ source,
620
+ parent_path,
621
+ str(overwrite),
622
+ )
623
+ except Exception as err:
624
+ self.logger.exception("Failed to upload %s to %s: %s", source, parent_path, err)
625
+ self.error_label.setText(f"Failed to upload {source}. Consult the logs.")