ibridgesgui 2.1.0__tar.gz → 2.1.2__tar.gz

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 (158) hide show
  1. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/PKG-INFO +1 -1
  2. ibridgesgui-2.1.2/coverage.svg +1 -0
  3. ibridgesgui-2.1.2/docs/userdoc.qmd +148 -0
  4. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/_version.py +3 -3
  5. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/browsertab/browser.py +47 -1
  6. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/browsertab/browser_controller.py +7 -1
  7. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/browsertab/irods_browser_service.py +108 -14
  8. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/config.py +4 -3
  9. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/gui_utils.py +5 -3
  10. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/info.py +8 -1
  11. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/irods_tree_model.py +2 -1
  12. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/login.py +6 -3
  13. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/mainmenu/main_window.py +3 -2
  14. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/mainmenu/tab_manager.py +2 -1
  15. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/__init__.py +2 -0
  16. ibridgesgui-2.1.2/ibridgesgui/popup_widgets/resc_tree.py +98 -0
  17. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/upload_data.py +3 -2
  18. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/searchtab/search.py +2 -2
  19. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/searchtab/search_controller.py +6 -5
  20. ibridgesgui-2.1.2/ibridgesgui/ui_files/rescTree.py +97 -0
  21. ibridgesgui-2.1.2/ibridgesgui/ui_files/rescTree.ui +101 -0
  22. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabBrowser.py +27 -21
  23. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabBrowser.ui +2 -2
  24. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabInfo.py +11 -5
  25. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabInfo.ui +7 -0
  26. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/uploadData.py +4 -4
  27. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/uploadData.ui +1 -1
  28. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui.egg-info/PKG-INFO +1 -1
  29. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui.egg-info/SOURCES.txt +5 -0
  30. ibridgesgui-2.1.2/ibridgesgui.egg-info/scm_file_list.json +150 -0
  31. ibridgesgui-2.1.2/ibridgesgui.egg-info/scm_version.json +8 -0
  32. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/pyproject.toml +6 -1
  33. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/browser/test_irods_browser_service.py +6 -4
  34. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/search/conftest.py +6 -7
  35. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/search/test_search_controller.py +2 -3
  36. ibridgesgui-2.1.0/coverage.svg +0 -1
  37. ibridgesgui-2.1.0/docs/userdoc.qmd +0 -138
  38. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.flake8 +0 -0
  39. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  40. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  41. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/dependabot.yml +0 -0
  42. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/workflows/linter.yml +0 -0
  43. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/workflows/nuitka_release.yml +0 -0
  44. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/workflows/publish.yml +0 -0
  45. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/workflows/pypi_release.yml +0 -0
  46. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.github/workflows/tests.yml +0 -0
  47. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/.gitignore +0 -0
  48. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/LICENSE +0 -0
  49. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/README.md +0 -0
  50. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/_quarto.yml +0 -0
  51. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/build_tools/build_script.py +0 -0
  52. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/build_tools/ui_to_py.py +0 -0
  53. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/developers.qmd +0 -0
  54. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/faq.qmd +0 -0
  55. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/getting-started.qmd +0 -0
  56. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/info.qmd +0 -0
  57. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/add_check_config.png +0 -0
  58. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/add_new_config.png +0 -0
  59. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/browser.png +0 -0
  60. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/login.png +0 -0
  61. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/metadata.png +0 -0
  62. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/permissions.png +0 -0
  63. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/replicas.png +0 -0
  64. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/search.png +0 -0
  65. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/start_ibridges.png +0 -0
  66. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/docs/screenshots/sync.png +0 -0
  67. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/__init__.py +0 -0
  68. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/__main__.py +0 -0
  69. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/browsertab/__init__.py +0 -0
  70. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/browsertab/browser_model.py +0 -0
  71. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/arrow-left.png +0 -0
  72. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/arrow-right.png +0 -0
  73. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/arrow-up.png +0 -0
  74. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/christmas-logo.png +0 -0
  75. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/home.png +0 -0
  76. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/iBridges.ico +0 -0
  77. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/loading_circle.gif +0 -0
  78. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/logo.png +0 -0
  79. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/nosync.png +0 -0
  80. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/refresh.png +0 -0
  81. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/icons/syncing.png +0 -0
  82. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/logviewer.py +0 -0
  83. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/mainmenu/__init__.py +0 -0
  84. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/mainmenu/config_manager.py +0 -0
  85. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/mainmenu/plugin_manager.py +0 -0
  86. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/mainmenu/session_manager.py +2 -2
  87. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/md_schemas/ibridges_metadata_schema.json +0 -0
  88. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/base.py +0 -0
  89. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/check_config.py +1 -1
  90. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/create_collection.py +1 -1
  91. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/create_directory.py +0 -0
  92. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/download_data.py +1 -1
  93. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/popup_widgets/rename_item.py +1 -1
  94. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/searchtab/__init__.py +0 -0
  95. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/searchtab/search_model.py +0 -0
  96. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/synctab/__init__.py +0 -0
  97. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/synctab/sync.py +0 -0
  98. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/synctab/sync_controller.py +1 -1
  99. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/synctab/sync_model.py +0 -0
  100. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/threads.py +2 -2
  101. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/MainMenu.py +0 -0
  102. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/MainMenu.ui +0 -0
  103. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/__init__.py +0 -0
  104. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/configCheck.py +0 -0
  105. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/configCheck.ui +0 -0
  106. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/createCollection.py +0 -0
  107. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/createCollection.ui +0 -0
  108. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/downloadData.py +0 -0
  109. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/downloadData.ui +0 -0
  110. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/irodsLogin.py +0 -0
  111. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/irodsLogin.ui +0 -0
  112. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/renameItem.py +0 -0
  113. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/renameItem.ui +0 -0
  114. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabLogging.py +0 -0
  115. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabLogging.ui +0 -0
  116. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabSearch.py +0 -0
  117. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabSearch.ui +0 -0
  118. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabSync.py +0 -0
  119. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/tabSync.ui +0 -0
  120. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/transferData.py +0 -0
  121. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/transferData.ui +0 -0
  122. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/welcome.py +0 -0
  123. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/ui_files/welcome.ui +0 -0
  124. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui/welcome.py +0 -0
  125. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui.egg-info/dependency_links.txt +0 -0
  126. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui.egg-info/requires.txt +0 -0
  127. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/ibridgesgui.egg-info/top_level.txt +0 -0
  128. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/index.qmd +0 -0
  129. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/setup.cfg +0 -0
  130. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/browser/conftest.py +0 -0
  131. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/browser/test_browser_controller.py +0 -0
  132. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/browser/test_browser_model.py +0 -0
  133. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/conftest.py +0 -0
  134. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/gui/test_info_tab.py +0 -0
  135. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/gui/test_login.py +0 -0
  136. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/gui/test_main.py +0 -0
  137. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/main_window/conftest.py +0 -0
  138. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/main_window/test_config_manager.py +0 -0
  139. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/main_window/test_main_window.py +0 -0
  140. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/main_window/test_session_manager.py +0 -0
  141. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/main_window/test_tab_manager.py +0 -0
  142. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/popup/conftest.py +0 -0
  143. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/popup/test_check_config.py +0 -0
  144. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/popup/test_download_data.py +0 -0
  145. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/popup/test_simple_dialogs.py +0 -0
  146. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/popup/test_transfer_dialog_base.py +0 -0
  147. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/popup/test_upload_data.py +0 -0
  148. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/search/test_search_model.py +0 -0
  149. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/sync/conftest.py +0 -0
  150. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/sync/test_sync_controller.py +0 -0
  151. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/sync/test_sync_model.py +0 -0
  152. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/threads/conftest.py +0 -0
  153. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/threads/test_search_thread.py +0 -0
  154. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/threads/test_sync_thread.py +0 -0
  155. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/threads/test_transfer_thread.py +0 -0
  156. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/unit/test_config.py +0 -0
  157. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/unit/test_gui_utils.py +0 -0
  158. {ibridgesgui-2.1.0 → ibridgesgui-2.1.2}/tests/unit/test_irods_tree_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibridgesgui
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Graphical user interface for accessing data and metadata on iRODS servers.
5
5
  Author-email: Christine Staiger <c.staiger@uu.nl>
6
6
  License: GNU LESSER GENERAL PUBLIC LICENSE
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 83.18%"><title>coverage: 83.18%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#67ac09"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="865" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">83.18%</text><text x="865" y="150" fill-opacity=".3" textLength="430">83.18%</text></g><text x="865" y="140" textLength="430">83.18%</text></g></g></svg>
@@ -0,0 +1,148 @@
1
+ # iBridges User Documentation
2
+
3
+ ## Synopsis
4
+
5
+ iBridges is a **GUI for any data‑management platform based on iRODS**. It exposes iRODS functionality through a graphical interface. The GUI groups related functionality into separate views, each representing a specific function area.
6
+
7
+ Default iBridges‑GUI views:
8
+
9
+ - **Browser** — browse iRODS collections, inspect data objects and collections, **edit metadata and permissions**, and **delete data**.
10
+ - **Data Synchronisation** — synchronise local directories with their corresponding iRODS collections.
11
+ - **Search** — search for data collections and objects.
12
+ - **Info** — summarises information such as the user’s role, groups, and the storage resources on the iRODS server, including their status and available capacity.
13
+
14
+ ## Views and Plugins
15
+
16
+ In the main menu (next to the configuration menu), you will find a drop‑down menu called **“Views and Plugins”**. Selecting or deselecting items in this menu will show or hide the corresponding tabs in the GUI. All checked tabs and plugins will automatically be restored in the next session.
17
+
18
+ On first launch, the default views `Welcome`, `Browser`, `Synchronise Data`, `Search`, `Info`, and `Logs` are shown.
19
+
20
+ ## Browsing through your iRODS collections
21
+
22
+ After logging into iRODS, the first view you see is the **Browser**. This view has three sections:
23
+
24
+ 1. Navigation
25
+ 2. Listing
26
+ 3. Detailed inspection of data objects and collections
27
+
28
+ ![](screenshots/browser.png)
29
+
30
+ ### Navigation
31
+
32
+ - The navigation bar contains an **editable field** showing your current iRODS path. On startup, it points to your `irods_home` from the configuration file.
33
+ You can **navigate anywhere in the iRODS tree** by editing this field and pressing `<Enter>`.
34
+
35
+ - The **three buttons** next to the path field are:
36
+ - `Refresh` (circular arrows) — reload the listing
37
+ - `Up` (arrow) — go one level up
38
+ - `Home` (house icon) — jump to your home collection
39
+
40
+ - `Create Collection` creates a new collection at the path shown above.
41
+
42
+ - `Upload` opens a dialog to select files for upload. The destination is the path in the navigation field. You may also specify metadata files to be extracted upon upload and be stored as iRODS metadata.
43
+
44
+ - `Download` opens a dialog to choose a local destination. You may also export metadata alongside the download.
45
+ To download, select an item in the table below.
46
+ For downloading multiple items, see the **Search** section.
47
+
48
+ - `Delete` removes the selected collection or data object.
49
+ **Note: deleted data is permanently removed from the iRODS instance.**
50
+
51
+ ### Listing
52
+
53
+ The table below the navigation bar lists all collections and data objects in the current path.
54
+
55
+ Columns:
56
+
57
+ - **Status** — `-C` for collections, or the replica status for data objects.
58
+ If a data object’s status is not `good`, the data is compromised. Contact your iRODS administrator. Do not use such data.
59
+ - **Name** — collection or data object name
60
+ - **Size** — size in bytes
61
+ - **Checksum** — a hash used to verify data integrity
62
+ - **Created / Modified** — timestamps for creation and last modification
63
+
64
+ - *Single click* loads details in the section below.
65
+ - *Double click* on a collection navigates into it.
66
+
67
+ ### Inspect collections and data objects
68
+
69
+ This section shows detailed information about the selected item.
70
+
71
+ - **Preview** — lists the contents of a collection or shows the first lines of text, JSON, or CSV files.
72
+
73
+ - **Metadata** — displays iRODS metadata (key, value, units).
74
+ - *Single click* loads the metadata entry into the editable fields.
75
+ - To create metadata, fill in *key*, *value*, and *units*, then click `Add`.
76
+ Multiple entries with the same key but different values are allowed.
77
+ - `Update` updates the current selected metadata entry.
78
+ - `Delete` removes the metadata entry matching the fields.
79
+
80
+ ![](screenshots/metadata.png)
81
+
82
+ - **Permissions** — shows access rights for the selected item.
83
+ The drop‑down menu `Access name` exposes the following rights:
84
+
85
+ | Rights/Action | upload file | write file | delete file | add metadata | update metadata | remove metadata |
86
+ |---------------|-------------|------------|-------------|--------------|------------------|------------------|
87
+ | delete_object | ✅ also `imkdir` | ✅ | ✅/❌ | ✅ | ❌ | ✅ |
88
+ | modify_object | ✅ also `imkdir` | ✅ | ✅/❌ | ✅ | ✅ | ✅ |
89
+ | create_object | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
90
+ | read_object | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
91
+
92
+ ![](screenshots/permissions.png)
93
+
94
+ The **Creator** label shows who created the selected item.
95
+
96
+ Collections support `inherit` / `noinherit`.
97
+ `inherit` applies the collection’s permissions to newly added items.
98
+
99
+ To apply permission changes recursively to all existing members of a collection, enable `Recursive`.
100
+
101
+ - **Replicas** — shows the storage resources where a data object is stored.
102
+ Collections have no replicas.
103
+
104
+ ![](screenshots/replicas.png)
105
+
106
+ Replica information is mainly for sysadmins. If a replica status is not `good`, send them this information.
107
+
108
+ ## Data Synchronisation
109
+
110
+ `Synchronise Data` provides tools to **synchronise a local folder with an iRODS collection**.
111
+ Synchronisation works both ways:
112
+
113
+ - Local → iRODS (right‑pointing arrow)
114
+ - iRODS → Local (left‑pointing arrow)
115
+
116
+ Before synchronisation, differences are calculated and shown in the text field below.
117
+ **Note:** calculating differences may take some time.
118
+
119
+ ![](screenshots/sync.png)
120
+
121
+ Synchronisation works only for collections/directories, not for single files.
122
+
123
+ ## Search
124
+
125
+ `Search` provides a form where you can enter path and metadata criteria.
126
+ Click `Search` to list matching collections and data objects.
127
+
128
+ ![](screenshots/search.png)
129
+
130
+ ### Search fields
131
+
132
+ - The path at the top defines where the search is performed.
133
+ It must exist; by default, your home collection is used.
134
+
135
+ - `Obj/Coll name` filters by name.
136
+ Example: entering `demo` finds all items named *demo*.
137
+
138
+ - Example above: searching for all `*.txt` objects in `my_books` using `my_books/%.txt`.
139
+
140
+ - Metadata filters narrow results further.
141
+ Example: key `author`, value containing `Doyle` → `%Doyle%`.
142
+
143
+ **Note: the wildcard is `%`.**
144
+
145
+ ### Search results
146
+
147
+ - *Single click* or *Select all* → click `Download` to choose a destination and start downloading.
148
+ - *Double click* → open the item in the `Browser` for further inspection.
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.1.0'
22
- __version_tuple__ = version_tuple = (2, 1, 0)
21
+ __version__ = version = '2.1.2'
22
+ __version_tuple__ = version_tuple = (2, 1, 2)
23
23
 
24
- __commit_id__ = commit_id = 'g97bdf8817'
24
+ __commit_id__ = commit_id = 'gda2566526'
@@ -21,9 +21,55 @@ class Browser(PySide6.QtWidgets.QWidget, Ui_tabBrowser):
21
21
  else:
22
22
  load_ui(UI_FILE_DIR / "tabBrowser.ui", self)
23
23
 
24
+ self.setup_tooltips()
24
25
  self.controller = BrowserController(self, session, app_name)
25
26
  self.controller.init_browser()
26
27
 
28
+ def setup_tooltips(self):
29
+ """Add tool tips to interactive elements."""
30
+ # --- Navigation ---
31
+ self.input_path.setToolTip("Enter an iRODS path and press Enter to navigate.")
32
+ self.refresh_button.setToolTip("Reload the current collection or data object.")
33
+ self.home_button.setToolTip("Go to your iRODS home collection.")
34
+ self.parent_button.setToolTip("Move to the parent collection of the current path.")
35
+
36
+ # --- CRUD operations ---
37
+ self.upload_button.setToolTip("Upload a file or directory to the current iRODS path.")
38
+ self.download_button.setToolTip("Download the selected iRODS item.")
39
+ self.create_coll_button.setToolTip("Create a new collection inside the current path.")
40
+ self.rename_button.setToolTip("Rename the selected iRODS item.")
41
+ self.delete_button.setToolTip("Delete the selected iRODS item.")
42
+
43
+ # --- Browser table ---
44
+ self.browser_table.setToolTip(
45
+ "Browse collections and data objects. Double‑click to open an item."
46
+ )
47
+
48
+ # --- Info tabs ---
49
+ self.info_tabs.setToolTip(
50
+ "View metadata, ACLs, replicas, or a preview of the selected item."
51
+ )
52
+
53
+ # --- Metadata ---
54
+ self.meta_table.setToolTip(
55
+ "Metadata attached to this item. Click a row to load it for editing."
56
+ )
57
+ self.add_meta_button.setToolTip("Add a new metadata attribute to this item.")
58
+ self.update_meta_button.setToolTip("Update the selected metadata attribute.")
59
+ self.delete_meta_button.setToolTip("Delete the selected metadata attribute.")
60
+
61
+ # --- ACLs ---
62
+ self.acl_table.setToolTip(
63
+ "Access Control List entries. Click a row to load it for editing."
64
+ )
65
+ self.add_acl_button.setToolTip("Add or update an ACL entry for this item.")
66
+
67
+ # --- Replicas ---
68
+ self.replica_table.setToolTip("Replica information for this data object.")
69
+
70
+ # --- Preview ---
71
+ self.preview_browser.setToolTip("Preview the selected data object, if supported.")
72
+
27
73
  # --- Rendering helpers moved from controller ---
28
74
 
29
75
  def render_metadata(self, data, irods_path):
@@ -35,7 +81,7 @@ class Browser(PySide6.QtWidgets.QWidget, Ui_tabBrowser):
35
81
 
36
82
  populate_table(self.meta_table, len(data), data)
37
83
  current_tab = self.info_tabs.currentWidget().objectName()
38
- if current_tab == "metadata" and len(data) == 0:
84
+ if current_tab == "metadata" and len(data) == 0:
39
85
  self.no_meta_label.setText(f"Metadata for {irods_path} is empty.")
40
86
  self.meta_table.resizeColumnsToContents()
41
87
 
@@ -5,8 +5,8 @@ from typing import Optional
5
5
 
6
6
  import irods.exception
7
7
  import PySide6.QtWidgets
8
- from ibridges import IrodsPath
9
8
 
9
+ from ibridges import IrodsPath
10
10
  from ibridgesgui.browsertab.browser_model import BrowserModel
11
11
  from ibridgesgui.browsertab.irods_browser_service import IrodsBrowserService
12
12
  from ibridgesgui.gui_utils import populate_table
@@ -66,9 +66,11 @@ class BrowserController:
66
66
  self.model.set_path(irods_path)
67
67
  self.ui.input_path.setText(str(irods_path))
68
68
  self._load_browser_table()
69
+ self.ui.browser_table.clearSelection()
69
70
 
70
71
  def _refresh_browser(self) -> None:
71
72
  path = self.service.path_from_text(self.ui.input_path.text())
73
+ self.ui.browser_table.clearSelection()
72
74
  self._set_path(path)
73
75
 
74
76
  def _go_to_parent(self) -> None:
@@ -197,6 +199,10 @@ class BrowserController:
197
199
 
198
200
  self.ui.render_metadata(data, irods_path)
199
201
 
202
+ def get_possible_acl_strings(self):
203
+ """Retrieve a list of GUI accepted and cleaned iRODS ACL skills."""
204
+ return self.service.get_acl_strings()
205
+
200
206
  def _fill_acl_tab(self, irods_path, row):
201
207
  if row not in self.model.acl_cache:
202
208
  acls = self.service.get_acls(irods_path)
@@ -3,10 +3,11 @@
3
3
  import logging
4
4
  from typing import Iterable, Tuple
5
5
 
6
+ from irods.exception import CAT_NO_ACCESS_PERMISSION
7
+
6
8
  from ibridges import IrodsPath
7
9
  from ibridges.permissions import Permissions
8
10
  from ibridges.util import obj_replicas
9
-
10
11
  from ibridgesgui.gui_utils import get_irods_item
11
12
 
12
13
 
@@ -45,9 +46,26 @@ class IrodsBrowserService:
45
46
 
46
47
  # -------- preview ---------
47
48
 
49
+ def is_text_bytes(self, data: bytes) -> bool:
50
+ """Sniff if file contains text."""
51
+ # Null bytes → definitely binary
52
+ if b"\x00" in data:
53
+ return False
54
+
55
+ # Try common encodings
56
+ for enc in ("utf-8", "latin-1", "cp1252"):
57
+ try:
58
+ data.decode(enc)
59
+ return True
60
+ except UnicodeDecodeError:
61
+ continue
62
+
63
+ return False
64
+
65
+
48
66
  def compute_preview(self, irods_path: IrodsPath):
49
67
  """Return preview content for a collection or data object."""
50
- # Collections: list subcollections + objects
68
+ # Collections
51
69
  if irods_path.collection_exists():
52
70
  subcolls, objs = self.list_collection(irods_path)
53
71
  content = ["Collections:", "-----------------"]
@@ -56,10 +74,21 @@ class IrodsBrowserService:
56
74
  content.extend([do.name for do in objs])
57
75
  return content
58
76
 
59
- # Data objects: preview text-like files
77
+ # Data objects
60
78
  if irods_path.dataobject_exists():
61
- ext = irods_path.name.split(".")[-1] if "." in irods_path.name else ""
62
- if ext in ("txt", "json", "csv"):
79
+ try:
80
+ # Read only the first few KB for sniffing
81
+ with irods_path.open("r") as stream:
82
+ head = stream.read(4096)
83
+ except Exception as error:
84
+ return [
85
+ f"No Preview for: {irods_path}",
86
+ repr(error),
87
+ "Storage resource might be down.",
88
+ ]
89
+
90
+ # Decide based on content, not suffix
91
+ if self.is_text_bytes(head):
63
92
  try:
64
93
  return self.stream_obj(irods_path)
65
94
  except Exception as error:
@@ -68,6 +97,7 @@ class IrodsBrowserService:
68
97
  repr(error),
69
98
  "Storage resource might be down.",
70
99
  ]
100
+
71
101
  return [f"No Preview for: {irods_path}"]
72
102
 
73
103
  return [f"No Preview for: {irods_path}"]
@@ -90,9 +120,41 @@ class IrodsBrowserService:
90
120
  except Exception as err:
91
121
  raise RuntimeError(f"Failed to load metadata for {irods_path}: {err}") from err
92
122
 
123
+ def find_irods_exception(self, exc):
124
+ """Walk the exception chain to find the underlying iRODS exception."""
125
+ # NOTE: Work around for ibridges #412
126
+ seen = set()
127
+ stack = [exc]
128
+
129
+ while stack:
130
+ current = stack.pop()
131
+ if current in seen:
132
+ continue
133
+ seen.add(current)
134
+
135
+ # Found the real iRODS exception
136
+ if isinstance(current, CAT_NO_ACCESS_PERMISSION):
137
+ return current
138
+
139
+ # Walk deeper
140
+ if current.__cause__:
141
+ stack.append(current.__cause__)
142
+ if current.__context__:
143
+ stack.append(current.__context__)
144
+
145
+ return None
146
+
147
+
93
148
  def add_metadata(self, path: IrodsPath, key: str, value: str, units: str):
94
149
  """Add metadata to coll or obj."""
95
- path.meta.add(key, value, units)
150
+ try:
151
+ path.meta.add(key, value, units)
152
+ except Exception as err:
153
+ # user has no permission to modify metadata
154
+ irods_exc = self.find_irods_exception(err)
155
+ if irods_exc:
156
+ raise irods_exc from err
157
+ raise err
96
158
 
97
159
  def update_metadata(
98
160
  self,
@@ -105,20 +167,51 @@ class IrodsBrowserService:
105
167
  new_units: str,
106
168
  ):
107
169
  """Update a single AVU on a path."""
108
- # Retrieve the specific AVU
109
- avu = path.meta[old_key, old_value, old_units]
170
+ try:
171
+ # Retrieve the specific AVU
172
+ avu = path.meta[old_key, old_value, old_units]
110
173
 
111
- # Mutate it in place
112
- avu.key = new_key
113
- avu.value = new_value
114
- avu.units = new_units
174
+ # Add new and delete old
175
+ path.meta.add(new_key, new_value, new_units)
176
+ path.meta.delete(avu.key, avu.value, avu.units)
177
+ except Exception as err:
178
+ # user has no permission to modify metadata
179
+ irods_exc = self.find_irods_exception(err)
180
+ if irods_exc:
181
+ raise irods_exc from err
182
+ raise err
115
183
 
116
184
  def delete_metadata(self, path: IrodsPath, key: str, value: str, units: str):
117
185
  """Delete metadata."""
118
- path.meta.delete(key, value, units)
186
+ try:
187
+ path.meta.delete(key, value, units)
188
+ except Exception as err:
189
+ # user has no permission to modify metadata
190
+ irods_exc = self.find_irods_exception(err)
191
+ if irods_exc:
192
+ raise irods_exc from err
193
+ raise err
119
194
 
120
195
  # -------- ACLs / permissions --------
121
196
 
197
+ PERM_MAP = {
198
+ "read_object": "read",
199
+ "modify_object": "write",
200
+ "null": "delete permission",
201
+ "delete_object": "delete",
202
+ "create_object": "create"
203
+ }
204
+ REVERSE_PERM_MAP = {v: k for k, v in PERM_MAP.items()}
205
+
206
+ def get_acl_strings(self):
207
+ """Retrieve all possible acl modes from the irods instance and filter them."""
208
+ perm = Permissions(self.session, self.session.home)
209
+ # remove metadata permissions github.com/irods/irods/issues/6813
210
+ avail = [self.PERM_MAP.get(perm_str, perm_str)
211
+ for (perm_str, _) in perm.available_permissions.items()
212
+ if "metadata" not in perm_str]
213
+ return avail
214
+
122
215
  def normalize_acls(self, acls):
123
216
  """Normalize iRODS ACLs into UI-friendly form."""
124
217
  clean = []
@@ -152,8 +245,9 @@ class IrodsBrowserService:
152
245
  """Apply an ACL change to a collection or data object."""
153
246
  obj = get_irods_item(path)
154
247
  perms = Permissions(self.session, obj)
248
+ irods_access = self.REVERSE_PERM_MAP[access]
155
249
  perms.set(
156
- perm=access,
250
+ perm=irods_access,
157
251
  user=user_name,
158
252
  zone=user_zone,
159
253
  recursive=recursive,
@@ -19,9 +19,6 @@ from json import JSONDecodeError
19
19
  from pathlib import Path
20
20
  from typing import Union
21
21
 
22
- from ibridges.cli.config import IbridgesConf
23
- from ibridges.session import Session, _translate_irods_error
24
- from ibridges.util import open_irodsa
25
22
  from irods.auth.pam import PamLoginException
26
23
  from irods.connection import PlainTextPAMPasswordError
27
24
  from irods.exception import (
@@ -33,6 +30,10 @@ from irods.exception import (
33
30
  )
34
31
  from irods.session import iRODSSession
35
32
 
33
+ from ibridges.cli.config import IbridgesConf
34
+ from ibridges.session import Session, _translate_irods_error
35
+ from ibridges.util import open_irodsa
36
+
36
37
  try:
37
38
  from importlib_metadata import version # Python < 3.10
38
39
  except ImportError:
@@ -13,11 +13,11 @@ from typing import Iterable, Optional, Union
13
13
  import PySide6.QtCore
14
14
  import PySide6.QtUiTools
15
15
  import PySide6.QtWidgets
16
- from ibridges import IrodsPath
17
- from ibridges.executor import Operations
18
16
  from jsonschema import ValidationError, validate
19
17
 
20
18
  import ibridgesgui.md_schemas
19
+ from ibridges import IrodsPath
20
+ from ibridges.executor import Operations
21
21
  from ibridgesgui.config import get_last_ienv_path, is_session_from_config
22
22
 
23
23
  try:
@@ -160,7 +160,9 @@ def prep_session_for_copy(session, error_label) -> Optional[Path]:
160
160
  last_env_path = Path.home() / ".irods" / get_last_ienv_path()
161
161
  session_env_path = session.irods_session.env_file
162
162
 
163
- if last_env_path != session_env_path:
163
+ # irods session does not always contain the env path.
164
+ # this is the case when the session is created with a password
165
+ if session_env_path and (last_env_path != session_env_path):
164
166
  error_label.setText(
165
167
  "A different iRODS configuration was loaded outside this app. "
166
168
  "Restart the session to continue."
@@ -2,11 +2,12 @@
2
2
 
3
3
  import sys
4
4
 
5
- from ibridges.resources import Resources
6
5
  from PySide6 import QtCore, QtWidgets
7
6
 
7
+ from ibridges.resources import Resources
8
8
  from ibridgesgui.config import CONFIG_DIR
9
9
  from ibridgesgui.gui_utils import UI_FILE_DIR, load_ui, populate_table, populate_textfield
10
+ from ibridgesgui.popup_widgets.resc_tree import RescInfoDialog
10
11
  from ibridgesgui.ui_files.tabInfo import Ui_tabInfo
11
12
 
12
13
 
@@ -20,6 +21,7 @@ class Info(QtWidgets.QWidget, Ui_tabInfo):
20
21
  self.session = session
21
22
 
22
23
  self.refresh_button.clicked.connect(self.refresh_info)
24
+ self.inspect_button.clicked.connect(self.inspect_rescs_info)
23
25
  self.refresh_info()
24
26
 
25
27
  def _load_ui(self):
@@ -28,6 +30,11 @@ class Info(QtWidgets.QWidget, Ui_tabInfo):
28
30
  else:
29
31
  load_ui(UI_FILE_DIR / "tabInfo.ui", self)
30
32
 
33
+ def inspect_rescs_info(self):
34
+ """Open popup for rescs info."""
35
+ dlg = RescInfoDialog(self.session, self)
36
+ dlg.exec()
37
+
31
38
  def refresh_info(self):
32
39
  """Refetch infor from iRODS."""
33
40
  with self._wait_cursor():
@@ -2,9 +2,10 @@
2
2
 
3
3
  import irods
4
4
  import irods.exception
5
- from ibridges import IrodsPath
6
5
  from PySide6 import QtCore, QtGui, QtWidgets
7
6
 
7
+ from ibridges import IrodsPath
8
+
8
9
 
9
10
  class IrodsTreeModel(QtGui.QStandardItemModel):
10
11
  """Model for an iRODS tree view with lazy loading and path lookup."""
@@ -5,11 +5,11 @@ import os
5
5
  from pathlib import Path
6
6
  from typing import Any
7
7
 
8
- from ibridges import Session
9
- from ibridges.session import LoginError, PasswordError
10
8
  from irods.exception import ResourceDoesNotExist
11
9
  from PySide6.QtWidgets import QDialog, QLineEdit
12
10
 
11
+ from ibridges import Session
12
+ from ibridges.session import LoginError, PasswordError
13
13
  from ibridgesgui.config import (
14
14
  IRODSA,
15
15
  check_irods_config,
@@ -129,7 +129,10 @@ class LoginDialog(QDialog, Ui_irodsLogin):
129
129
  f.write(password)
130
130
  return Session(irods_env=env_path)
131
131
 
132
- return Session(irods_env=env_path, password=password)
132
+ session = Session(irods_env=env_path, password=password)
133
+ session.write_pam_password()
134
+
135
+ return session
133
136
 
134
137
  def _on_connect(self):
135
138
  self.error_label.clear()
@@ -5,7 +5,7 @@ import logging
5
5
  from pathlib import Path
6
6
 
7
7
  from PySide6.QtGui import QAction
8
- from PySide6.QtWidgets import QMainWindow, QMessageBox
8
+ from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox
9
9
 
10
10
  from ibridgesgui.gui_utils import UI_FILE_DIR, load_ui
11
11
  from ibridgesgui.mainmenu import (
@@ -98,7 +98,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
98
98
  self.session_manager.disconnect()
99
99
 
100
100
  def _on_exit(self) -> None:
101
- self.close()
101
+ #self.close()
102
+ QApplication.quit()
102
103
 
103
104
  def _on_create_env(self) -> None:
104
105
  widget = CheckConfig(self.logger, Path("~/.irods").expanduser())
@@ -38,7 +38,8 @@ class TabManager:
38
38
 
39
39
  def _create_search(self, session, app_name: str, _logger) -> Search:
40
40
  browser = self.loaded_tabs.get("Browser")
41
- return Search(session, app_name, browser)
41
+ browsercontroller = getattr(browser, "controller", None)
42
+ return Search(session, app_name, browsercontroller)
42
43
 
43
44
  def _create_info(self, session, _app_name, _logger) -> Info:
44
45
  return Info(session)
@@ -5,6 +5,7 @@ from ibridgesgui.popup_widgets.create_collection import CreateCollection
5
5
  from ibridgesgui.popup_widgets.create_directory import CreateDirectory
6
6
  from ibridgesgui.popup_widgets.download_data import DownloadData
7
7
  from ibridgesgui.popup_widgets.rename_item import Rename
8
+ from ibridgesgui.popup_widgets.resc_tree import RescInfoDialog
8
9
  from ibridgesgui.popup_widgets.upload_data import UploadData
9
10
 
10
11
  __all__ = [
@@ -14,4 +15,5 @@ __all__ = [
14
15
  "CheckConfig",
15
16
  "UploadData",
16
17
  "DownloadData",
18
+ "RescInfoDialog"
17
19
  ]