ibridgesgui 2.1.3__tar.gz → 2.2.0__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.
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/workflows/linter.yml +3 -3
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/workflows/publish.yml +1 -1
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/workflows/pypi_release.yml +3 -3
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/workflows/tests.yml +5 -13
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/PKG-INFO +4 -3
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/coverage.svg +1 -1
- ibridgesgui-2.2.0/docs/screenshots/manage_tickets.png +0 -0
- ibridgesgui-2.2.0/docs/screenshots/supply_ticket.png +0 -0
- ibridgesgui-2.2.0/docs/userdoc.qmd +255 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/_version.py +3 -3
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/browsertab/browser_controller.py +1 -3
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/browsertab/irods_browser_service.py +3 -4
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/info.py +13 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/login.py +6 -2
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/mainmenu/main_window.py +35 -8
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/mainmenu/session_manager.py +1 -1
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/mainmenu/tab_manager.py +0 -1
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/__init__.py +5 -1
- ibridgesgui-2.2.0/ibridgesgui/popup_widgets/manage_tickets.py +166 -0
- ibridgesgui-2.2.0/ibridgesgui/popup_widgets/supply_ticket.py +60 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/synctab/sync_controller.py +1 -1
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/threads.py +6 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/MainMenu.py +41 -9
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/MainMenu.ui +64 -4
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabInfo.py +126 -114
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabInfo.ui +153 -139
- ibridgesgui-2.2.0/ibridgesgui/ui_files/ticketEditor.py +211 -0
- ibridgesgui-2.2.0/ibridgesgui/ui_files/ticketEditor.ui +233 -0
- ibridgesgui-2.2.0/ibridgesgui/ui_files/ticketSupply.py +124 -0
- ibridgesgui-2.2.0/ibridgesgui/ui_files/ticketSupply.ui +159 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui.egg-info/PKG-INFO +4 -3
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui.egg-info/SOURCES.txt +10 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui.egg-info/requires.txt +1 -1
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui.egg-info/scm_file_list.json +10 -0
- ibridgesgui-2.2.0/ibridgesgui.egg-info/scm_version.json +8 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/pyproject.toml +3 -2
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/gui/test_info_tab.py +2 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/main_window/test_main_window.py +2 -2
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/main_window/test_session_manager.py +2 -2
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/popup/conftest.py +16 -21
- ibridgesgui-2.2.0/tests/popup/test_manage_tickets.py +409 -0
- ibridgesgui-2.2.0/tests/popup/test_supply_ticket.py +180 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/threads/test_transfer_thread.py +1 -8
- ibridgesgui-2.1.3/docs/userdoc.qmd +0 -157
- ibridgesgui-2.1.3/ibridgesgui.egg-info/scm_version.json +0 -8
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.flake8 +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/dependabot.yml +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.github/workflows/nuitka_release.yml +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/.gitignore +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/LICENSE +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/README.md +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_quarto.yml +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_styles/callouts.css +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_styles/code.css +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_styles/hyperlinks.css +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_styles/ibridges.css +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_styles/navbar.css +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_styles/search_icon.css +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/_styles/toc.css +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/build_tools/build_script.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/build_tools/ui_to_py.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/developers.qmd +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/faq.qmd +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/getting-started.qmd +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/info.qmd +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/add_check_config.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/add_new_config.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/browser.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/login.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/logo.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/metadata.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/permissions.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/replicas.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/search.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/start_ibridges.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/docs/screenshots/sync.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/__init__.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/__main__.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/browsertab/__init__.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/browsertab/browser.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/browsertab/browser_model.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/config.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/gui_utils.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/arrow-left.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/arrow-right.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/arrow-up.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/christmas-logo.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/home.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/iBridges.ico +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/loading_circle.gif +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/logo.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/nosync.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/refresh.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/icons/syncing.png +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/irods_tree_model.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/logviewer.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/mainmenu/__init__.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/mainmenu/config_manager.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/mainmenu/plugin_manager.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/md_schemas/ibridges_metadata_schema.json +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/base.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/check_config.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/create_collection.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/create_directory.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/download_data.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/rename_item.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/resc_tree.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/popup_widgets/upload_data.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/searchtab/__init__.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/searchtab/search.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/searchtab/search_controller.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/searchtab/search_model.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/synctab/__init__.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/synctab/sync.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/synctab/sync_model.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/__init__.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/configCheck.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/configCheck.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/createCollection.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/createCollection.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/downloadData.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/downloadData.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/irodsLogin.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/irodsLogin.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/renameItem.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/renameItem.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/rescTree.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/rescTree.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabBrowser.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabBrowser.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabLogging.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabLogging.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabSearch.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabSearch.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabSync.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/tabSync.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/transferData.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/transferData.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/uploadData.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/uploadData.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/welcome.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/ui_files/welcome.ui +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui/welcome.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui.egg-info/dependency_links.txt +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/ibridgesgui.egg-info/top_level.txt +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/index.qmd +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/setup.cfg +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/browser/conftest.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/browser/test_browser_controller.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/browser/test_browser_model.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/browser/test_irods_browser_service.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/conftest.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/gui/test_login.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/gui/test_main.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/main_window/conftest.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/main_window/test_config_manager.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/main_window/test_tab_manager.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/popup/test_check_config.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/popup/test_download_data.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/popup/test_simple_dialogs.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/popup/test_transfer_dialog_base.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/popup/test_upload_data.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/search/conftest.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/search/test_search_controller.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/search/test_search_model.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/sync/conftest.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/sync/test_sync_controller.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/sync/test_sync_model.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/threads/conftest.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/threads/test_search_thread.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/threads/test_sync_thread.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/unit/test_config.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/unit/test_gui_utils.py +0 -0
- {ibridgesgui-2.1.3 → ibridgesgui-2.2.0}/tests/unit/test_irods_tree_model.py +0 -0
|
@@ -29,7 +29,7 @@ jobs:
|
|
|
29
29
|
fail-fast: false
|
|
30
30
|
matrix:
|
|
31
31
|
os: [ubuntu-latest]
|
|
32
|
-
python-version: ["3.
|
|
32
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
33
33
|
include:
|
|
34
34
|
- os: windows-latest
|
|
35
35
|
python-version: "3.12"
|
|
@@ -38,10 +38,10 @@ jobs:
|
|
|
38
38
|
runs-on: ${{ matrix.os }}
|
|
39
39
|
|
|
40
40
|
steps:
|
|
41
|
-
- uses: actions/checkout@
|
|
41
|
+
- uses: actions/checkout@v5
|
|
42
42
|
|
|
43
43
|
- name: Set up Python ${{ matrix.python-version }}
|
|
44
|
-
uses: actions/setup-python@
|
|
44
|
+
uses: actions/setup-python@v5
|
|
45
45
|
with:
|
|
46
46
|
python-version: ${{ matrix.python-version }}
|
|
47
47
|
|
|
@@ -24,9 +24,9 @@ jobs:
|
|
|
24
24
|
runs-on: ubuntu-latest
|
|
25
25
|
|
|
26
26
|
steps:
|
|
27
|
-
- uses: actions/checkout@
|
|
27
|
+
- uses: actions/checkout@v5
|
|
28
28
|
- name: Set up Python
|
|
29
|
-
uses: actions/setup-python@
|
|
29
|
+
uses: actions/setup-python@v5
|
|
30
30
|
with:
|
|
31
31
|
python-version: '3.x'
|
|
32
32
|
- name: Install dependencies
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
- name: Build package
|
|
37
37
|
run: python -m build
|
|
38
38
|
- name: Publish package
|
|
39
|
-
uses: pypa/gh-action-pypi-publish@v1
|
|
39
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
40
40
|
with:
|
|
41
41
|
user: __token__
|
|
42
42
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -26,23 +26,21 @@ jobs:
|
|
|
26
26
|
fail-fast: true
|
|
27
27
|
matrix:
|
|
28
28
|
os: [ubuntu-latest]
|
|
29
|
-
python-version: ["3.
|
|
29
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
30
30
|
include:
|
|
31
31
|
- os: macos-latest
|
|
32
32
|
python-version: "3.13"
|
|
33
33
|
runs-on: ${{ matrix.os }}
|
|
34
34
|
steps:
|
|
35
35
|
- name: Checkout
|
|
36
|
-
uses: actions/checkout@
|
|
36
|
+
uses: actions/checkout@v6
|
|
37
37
|
|
|
38
38
|
- name: Set up Python
|
|
39
|
-
uses: actions/setup-python@
|
|
39
|
+
uses: actions/setup-python@v6
|
|
40
40
|
with:
|
|
41
41
|
python-version: ${{ matrix.python-version }}
|
|
42
42
|
|
|
43
|
-
# -----------------------------
|
|
44
43
|
# Linux-only Qt dependencies
|
|
45
|
-
# -----------------------------
|
|
46
44
|
- name: Install Qt system dependencies (Linux)
|
|
47
45
|
if: runner.os == 'Linux'
|
|
48
46
|
run: |
|
|
@@ -60,18 +58,14 @@ jobs:
|
|
|
60
58
|
libxcb-xfixes0 \
|
|
61
59
|
xvfb
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
# Install Python dependencies
|
|
65
|
-
# -----------------------------
|
|
61
|
+
|
|
66
62
|
- name: Install dependencies
|
|
67
63
|
run: |
|
|
68
64
|
python -m pip install --upgrade pip
|
|
69
65
|
python -m pip install .[test]
|
|
70
66
|
python -m pip install pytest pytest-cov
|
|
71
67
|
|
|
72
|
-
# -----------------------------
|
|
73
68
|
# Set Qt to offscreen mode
|
|
74
|
-
# -----------------------------
|
|
75
69
|
- name: Configure Qt for headless mode
|
|
76
70
|
run: |
|
|
77
71
|
echo "QT_QPA_PLATFORM=offscreen" >> $GITHUB_ENV
|
|
@@ -81,9 +75,7 @@ jobs:
|
|
|
81
75
|
echo "QT_QPA_PLATFORM=offscreen" >> $GITHUB_ENV
|
|
82
76
|
echo "QT_MAC_WANTS_LAYER=1" >> $GITHUB_ENV
|
|
83
77
|
|
|
84
|
-
|
|
85
|
-
# Run tests
|
|
86
|
-
# -----------------------------
|
|
78
|
+
|
|
87
79
|
- name: Run tests and coverage
|
|
88
80
|
run: |
|
|
89
81
|
python -m pytest --cov=ibridgesgui --cov-report=xml --cov-report=term-missing --cov-fail-under=70 tests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ibridgesgui
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
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
|
|
@@ -512,16 +512,17 @@ Project-URL: GitHub, https://github.com/chStaiger/iBridges-Gui
|
|
|
512
512
|
Project-URL: documentation, https://github.com/chStaiger/iBridges-Gui
|
|
513
513
|
Keywords: irods,data management,storage
|
|
514
514
|
Classifier: Programming Language :: Python :: 3
|
|
515
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
516
515
|
Classifier: Programming Language :: Python :: 3.10
|
|
517
516
|
Classifier: Programming Language :: Python :: 3.11
|
|
518
517
|
Classifier: Programming Language :: Python :: 3.12
|
|
518
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
519
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
519
520
|
Classifier: Development Status :: 3 - Alpha
|
|
520
521
|
Requires-Python: >=3.9
|
|
521
522
|
Description-Content-Type: text/markdown
|
|
522
523
|
License-File: LICENSE
|
|
523
524
|
Requires-Dist: PySide6>=6.8.1
|
|
524
|
-
Requires-Dist: ibridges<2.
|
|
525
|
+
Requires-Dist: ibridges<2.3,>=2.2.0
|
|
525
526
|
Requires-Dist: setproctitle==1.3.8
|
|
526
527
|
Requires-Dist: importlib-resources; python_version <= "3.10"
|
|
527
528
|
Requires-Dist: jsonschema
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage:
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 84.04%"><title>coverage: 84.04%</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">84.04%</text><text x="865" y="150" fill-opacity=".3" textLength="430">84.04%</text></g><text x="865" y="140" textLength="430">84.04%</text></g></g></svg>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "User Documentation"
|
|
3
|
+
format:
|
|
4
|
+
html:
|
|
5
|
+
toc: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Synopsis
|
|
9
|
+
|
|
10
|
+
iBridges‑GUI is a graphical interface for any iRODS‑based data management platform.
|
|
11
|
+
It exposes core iRODS functionality through a set of focused views, each designed for a specific task.
|
|
12
|
+
|
|
13
|
+
Default views:
|
|
14
|
+
|
|
15
|
+
- `Browser` — explore collections, inspect data objects, edit metadata and permissions, upload/download, delete.
|
|
16
|
+
- `Data Synchronisation` — synchronise local directories with iRODS collections.
|
|
17
|
+
- `Search` — locate collections and data objects using name or metadata filters.
|
|
18
|
+
- `Info` — display user information, groups, and server resources (status, capacity).
|
|
19
|
+
|
|
20
|
+
## Views and Plugins
|
|
21
|
+
|
|
22
|
+
The `Views and Plugins` menu (next to `Configure`) lets you show or hide tabs.
|
|
23
|
+
Checked views are restored automatically when you restart the application.
|
|
24
|
+
|
|
25
|
+
On first launch, the following tabs appear:
|
|
26
|
+
`Welcome`, `Browser`, `Synchronise Data`, `Search`, `Info`, and `Logs`.
|
|
27
|
+
|
|
28
|
+
## Browser
|
|
29
|
+
|
|
30
|
+
After logging in, the *Browser* is the first view you see.
|
|
31
|
+
It consists of three parts:
|
|
32
|
+
|
|
33
|
+
1. Navigation
|
|
34
|
+
2. Listing
|
|
35
|
+
3. Detailed inspection
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
### Navigation
|
|
40
|
+
|
|
41
|
+
- The path field shows your current iRODS location.
|
|
42
|
+
Edit it and press `<Enter>` to jump anywhere in the tree.
|
|
43
|
+
|
|
44
|
+
- Buttons:
|
|
45
|
+
- *Refresh* — reload the listing
|
|
46
|
+
- *Up* — go one level up
|
|
47
|
+
- *Home* — jump to your home collection
|
|
48
|
+
|
|
49
|
+
- *Create Collection* — create a new collection at the current path.
|
|
50
|
+
|
|
51
|
+
- *Upload* — select files to upload.
|
|
52
|
+
Optional: include metadata files to extract and store as iRODS metadata.
|
|
53
|
+
|
|
54
|
+
- *Download* — choose a local destination.
|
|
55
|
+
Optional: export metadata alongside the download.
|
|
56
|
+
Select an item in the table to download it.
|
|
57
|
+
For downloading multiple items, use the *Search* view.
|
|
58
|
+
|
|
59
|
+
- *Delete* — permanently remove the selected item from iRODS.
|
|
60
|
+
|
|
61
|
+
### Listing
|
|
62
|
+
|
|
63
|
+
The table lists all collections and data objects at the current path.
|
|
64
|
+
|
|
65
|
+
Columns:
|
|
66
|
+
|
|
67
|
+
- Status — `-C` for collections; replica status for data objects.
|
|
68
|
+
If a data object’s status is not `good`, the data is compromised. Contact your iRODS administrator.
|
|
69
|
+
- Name
|
|
70
|
+
- Size
|
|
71
|
+
- Checksum
|
|
72
|
+
- Created / Modified
|
|
73
|
+
|
|
74
|
+
Actions:
|
|
75
|
+
|
|
76
|
+
- *Single click* — load details below.
|
|
77
|
+
- *Double click* — open a collection.
|
|
78
|
+
|
|
79
|
+
## Inspect collections and data objects
|
|
80
|
+
|
|
81
|
+
This section shows detailed information about the selected item.
|
|
82
|
+
|
|
83
|
+
### Preview
|
|
84
|
+
|
|
85
|
+
Displays collection contents or the first lines of text/JSON/CSV files.
|
|
86
|
+
|
|
87
|
+
### Metadata
|
|
88
|
+
|
|
89
|
+
Shows metadata as key–value–unit triples.
|
|
90
|
+
|
|
91
|
+
- *Single click* loads an entry into the editable fields.
|
|
92
|
+
- *Add* — create a new entry.
|
|
93
|
+
- *Update* — modify the selected entry.
|
|
94
|
+
- *Delete* — remove the entry matching the fields.
|
|
95
|
+
|
|
96
|
+

|
|
97
|
+
|
|
98
|
+
### Permissions
|
|
99
|
+
|
|
100
|
+
Shows access rights for the selected item.
|
|
101
|
+
|
|
102
|
+

|
|
103
|
+
|
|
104
|
+
- The *Access name* dropdown lists available rights.
|
|
105
|
+
- Creator shows who created the item.
|
|
106
|
+
- Collections support `inherit` / `noinherit`.
|
|
107
|
+
`inherit` applies collection permissions to newly added items.
|
|
108
|
+
- Enable *Recursive* to apply changes to all existing members.
|
|
109
|
+
|
|
110
|
+
### Replicas
|
|
111
|
+
|
|
112
|
+
Shows the storage resources where a data object is stored.
|
|
113
|
+
Collections do not have replicas.
|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
If a replica status is not `good`, contact your iRODS administrator.
|
|
118
|
+
|
|
119
|
+
## Data Synchronisation
|
|
120
|
+
|
|
121
|
+
`Synchronise Data` lets you synchronise a local directory with an iRODS collection.
|
|
122
|
+
|
|
123
|
+
- Local → iRODS (*right arrow*)
|
|
124
|
+
- iRODS → Local (*left arrow*)
|
|
125
|
+
|
|
126
|
+
Before synchronising, differences are calculated and shown in the text field.
|
|
127
|
+
This may take some time for large directories.
|
|
128
|
+
|
|
129
|
+

|
|
130
|
+
|
|
131
|
+
Synchronisation works only for directories/collections, not individual files.
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
## Search
|
|
135
|
+
|
|
136
|
+
The `Search` view allows you to filter by path, name, and metadata.
|
|
137
|
+
|
|
138
|
+

|
|
139
|
+
|
|
140
|
+
### Search fields
|
|
141
|
+
|
|
142
|
+
- `Path` — defines where the search is performed.
|
|
143
|
+
Must exist; defaults to your home collection.
|
|
144
|
+
|
|
145
|
+
- `Obj/Coll name` — filter by name.
|
|
146
|
+
Example: `demo` finds all items named *demo*.
|
|
147
|
+
|
|
148
|
+
- Wildcards use `%`.
|
|
149
|
+
Example: `my_books/%.txt` finds all `.txt` files in `my_books`.
|
|
150
|
+
|
|
151
|
+
- Metadata filters — narrow results further.
|
|
152
|
+
Example: key `author`, value `%Doyle%`.
|
|
153
|
+
|
|
154
|
+
### Search results
|
|
155
|
+
|
|
156
|
+
- *Single click* or *Select all* → click *Download* to choose a destination.
|
|
157
|
+
- *Double click* → open the item in the `Browser` for inspection.
|
|
158
|
+
|
|
159
|
+
## Data Sharing with iRODS tickets
|
|
160
|
+
|
|
161
|
+
### What are iRODS tickets
|
|
162
|
+
|
|
163
|
+
An iRODS ticket is a short string, for example `kpIwGA1UZN0LFnM`, that gives
|
|
164
|
+
access to one collection or data object without changing any permissions
|
|
165
|
+
and without the need for an account. Anyone who has the ticket string and the
|
|
166
|
+
path it belongs to can read the shared data.
|
|
167
|
+
|
|
168
|
+
- A ticket is either a read ticket or a write ticket.
|
|
169
|
+
A write ticket also allows uploads and changes.
|
|
170
|
+
- A ticket can have an expiry date. After that date it no longer
|
|
171
|
+
gives access to the data.
|
|
172
|
+
- Tickets are stored on the iRODS server and belong to the user who created
|
|
173
|
+
them. The owner can delete a ticket at any time, which revokes the access.
|
|
174
|
+
|
|
175
|
+
Typical uses are sharing a dataset with external collaborators, publishing data
|
|
176
|
+
for download through an `anonymous` account, and giving temporary access.
|
|
177
|
+
|
|
178
|
+
**Always share the ticket string together with the iRODS path it was made for.**
|
|
179
|
+
|
|
180
|
+
### Create tickets
|
|
181
|
+
|
|
182
|
+

|
|
183
|
+
|
|
184
|
+
Open the ticket manager from the menu bar `Data Access`. The view has two parts: the iRODS tree
|
|
185
|
+
and next to it the ticket settings to choose from, and below the table with your existing tickets.
|
|
186
|
+
|
|
187
|
+
To create a ticket:
|
|
188
|
+
|
|
189
|
+
1. Select the collection or data object you want to share in the iRODS tree.
|
|
190
|
+
2. Choose the access mode, *read* or *write*.
|
|
191
|
+
3. Choose when the ticket expires. Pick a date in the calendar, or check the box to create a ticket that does not expire. The calendar is disabled while the box is checked.
|
|
192
|
+
4. Click *Create ticket*. The new ticket appears in the table.
|
|
193
|
+
5. Send the ticket string and the path to the person you want to share with.
|
|
194
|
+
|
|
195
|
+
The table lists all your tickets:
|
|
196
|
+
|
|
197
|
+
- Ticket — the ticket string to share.
|
|
198
|
+
- iRODS path — the shared collection or data object. Hover over the path
|
|
199
|
+
to see it in full.
|
|
200
|
+
- Expires — the expiry date, or `never`.
|
|
201
|
+
- ❌ — delete the ticket and revoke the access.
|
|
202
|
+
|
|
203
|
+
Good to know:
|
|
204
|
+
|
|
205
|
+
- A ticket stops working at the start of the selected day. If it should
|
|
206
|
+
still work during the whole day, select the following day.
|
|
207
|
+
- Share write tickets with care, since they allow changes to the shared
|
|
208
|
+
collection.
|
|
209
|
+
- Delete tickets that are no longer needed. Do not rely on the expiry date alone.
|
|
210
|
+
|
|
211
|
+
### Use tickets
|
|
212
|
+
|
|
213
|
+
You can use tickets as user of the system or as anonymous user.
|
|
214
|
+
|
|
215
|
+

|
|
216
|
+
|
|
217
|
+
#### Supply a ticket
|
|
218
|
+
|
|
219
|
+
To use a ticket, open the *Supply Ticket* dialog and fill in:
|
|
220
|
+
|
|
221
|
+
- Ticket — the ticket string you received.
|
|
222
|
+
- iRODS path — the path of the collection or data object the ticket was
|
|
223
|
+
made for.
|
|
224
|
+
|
|
225
|
+
Click *OK*. If the ticket and the path match, the dialog closes and the
|
|
226
|
+
shared location becomes your current location in the `Browser`. You can then browse, view metadata and download data. With a write ticket you can also
|
|
227
|
+
upload.
|
|
228
|
+
|
|
229
|
+
#### As a user of the system
|
|
230
|
+
|
|
231
|
+
If you already have an account, log in as usual and supply the ticket. The
|
|
232
|
+
ticket gives you access to the shared data on top of your own permissions.
|
|
233
|
+
|
|
234
|
+
#### As an anonymous user
|
|
235
|
+
|
|
236
|
+
Collaborators without an account use the `anonymous` user of the iRODS server.
|
|
237
|
+
Create an iRODS environment file for it with the environment editor (*Configure*). The user name is `anonymous` and works without providing a password. Your iRODS administrator can provide you with the correct irods environment settings.
|
|
238
|
+
|
|
239
|
+
Once you log in with this environment you can supply the ticket as described above.
|
|
240
|
+
Without a ticket, an anonymous user cannot see any data.
|
|
241
|
+
|
|
242
|
+
#### Things to keep in mind
|
|
243
|
+
|
|
244
|
+
- A ticket lasts as long as your session. If you log out or restart the application, supply the ticket again.
|
|
245
|
+
|
|
246
|
+
- Use one ticket per session. To switch to another ticket, log in again first.
|
|
247
|
+
|
|
248
|
+
- A ticket on a data object only gives access to that object, not to the other items in its collection. The Browser opens the parent collection, where the listing may be empty. Type the full path of the object in the path field of the Browser to reach it.
|
|
249
|
+
|
|
250
|
+
- Expired tickets are still accepted by the dialog, and the shared path may still be visible, but downloading data fails with `CAT_TICKET_EXPIRED`. Ask the owner for a new ticket.
|
|
251
|
+
|
|
252
|
+
- Deleted tickets: if the owner deletes the ticket while you are using it, further actions fail with `CAT_TICKET_INVALID`.
|
|
253
|
+
|
|
254
|
+
The same functionality is available from the command line and in Python. See the
|
|
255
|
+
*Working with Tickets* tutorial in the [iBridges documentation](https://ibridges.readthedocs.io/en/latest/).
|
|
@@ -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.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.2.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g13809c1c4'
|
|
@@ -248,7 +248,7 @@ class BrowserController:
|
|
|
248
248
|
label_to_acl = {
|
|
249
249
|
"Newly added items to collection will inherit permissions": "inherit",
|
|
250
250
|
"Remove inheritance.": "noinherit",
|
|
251
|
-
"delete": "null",
|
|
251
|
+
#"delete": "null",
|
|
252
252
|
}
|
|
253
253
|
acl_value = label_to_acl.get(acc_label, acc_label)
|
|
254
254
|
|
|
@@ -265,7 +265,6 @@ class BrowserController:
|
|
|
265
265
|
if not acc_label:
|
|
266
266
|
self.ui.error_label.setText("Please provide an access level.")
|
|
267
267
|
return
|
|
268
|
-
|
|
269
268
|
try:
|
|
270
269
|
self.service.set_acl(
|
|
271
270
|
irods_path,
|
|
@@ -275,7 +274,6 @@ class BrowserController:
|
|
|
275
274
|
recursive=recursive,
|
|
276
275
|
)
|
|
277
276
|
|
|
278
|
-
# NEW: use model helper instead of manual cache manipulation
|
|
279
277
|
self.model.invalidate_acls(row)
|
|
280
278
|
|
|
281
279
|
self._fill_current_info_tab()
|
|
@@ -27,7 +27,7 @@ class IrodsBrowserService:
|
|
|
27
27
|
|
|
28
28
|
def home_path(self) -> IrodsPath:
|
|
29
29
|
"""Get home path."""
|
|
30
|
-
return IrodsPath(self.session)
|
|
30
|
+
return IrodsPath(self.session, self.session.home)
|
|
31
31
|
|
|
32
32
|
def parent_path(self, text: str) -> IrodsPath:
|
|
33
33
|
"""Determine parent."""
|
|
@@ -197,8 +197,7 @@ class IrodsBrowserService:
|
|
|
197
197
|
PERM_MAP = {
|
|
198
198
|
"read_object": "read",
|
|
199
199
|
"modify_object": "write",
|
|
200
|
-
"null": "delete
|
|
201
|
-
"delete_object": "delete",
|
|
200
|
+
"null": "delete",
|
|
202
201
|
"create_object": "create"
|
|
203
202
|
}
|
|
204
203
|
REVERSE_PERM_MAP = {v: k for k, v in PERM_MAP.items()}
|
|
@@ -252,7 +251,7 @@ class IrodsBrowserService:
|
|
|
252
251
|
|
|
253
252
|
obj = get_irods_item(path)
|
|
254
253
|
perms = Permissions(self.session, obj)
|
|
255
|
-
irods_access = self.REVERSE_PERM_MAP
|
|
254
|
+
irods_access = self.REVERSE_PERM_MAP.get(access, access)
|
|
256
255
|
perms.set(
|
|
257
256
|
perm=irods_access,
|
|
258
257
|
user=user_name,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Info tab."""
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
5
|
|
|
5
6
|
from PySide6 import QtCore, QtWidgets
|
|
6
7
|
|
|
@@ -52,6 +53,10 @@ class Info(QtWidgets.QWidget, Ui_tabInfo):
|
|
|
52
53
|
"default_resc": self.session.default_resc,
|
|
53
54
|
"server": self.session.host,
|
|
54
55
|
"version": ".".join(map(str, self.session.server_version)),
|
|
56
|
+
"client_version": (
|
|
57
|
+
f"iBridges GUI {self._get_version('ibridgesgui')} / "
|
|
58
|
+
f"iBridges API {self._get_version('ibridges')}"
|
|
59
|
+
),
|
|
55
60
|
"resources": Resources(self.session).root_resources,
|
|
56
61
|
}
|
|
57
62
|
|
|
@@ -64,6 +69,7 @@ class Info(QtWidgets.QWidget, Ui_tabInfo):
|
|
|
64
69
|
self.resc_label.setText(info["default_resc"])
|
|
65
70
|
self.server_label.setText(info["server"])
|
|
66
71
|
self.version_label.setText(info["version"])
|
|
72
|
+
self.client_version_label.setText(info["client_version"])
|
|
67
73
|
|
|
68
74
|
resources = [
|
|
69
75
|
tuple("" if v is None else v for v in row)
|
|
@@ -74,6 +80,13 @@ class Info(QtWidgets.QWidget, Ui_tabInfo):
|
|
|
74
80
|
|
|
75
81
|
self._autosize_columns(self.resc_table)
|
|
76
82
|
|
|
83
|
+
def _get_version(self, dist_name):
|
|
84
|
+
"""Return the installed version of a distribution, or 'unknown'."""
|
|
85
|
+
try:
|
|
86
|
+
return version(dist_name)
|
|
87
|
+
except PackageNotFoundError:
|
|
88
|
+
return "unknown"
|
|
89
|
+
|
|
77
90
|
def _autosize_columns(self, table):
|
|
78
91
|
header = table.horizontalHeader()
|
|
79
92
|
for col in range(header.count()):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Login widget."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import json
|
|
4
5
|
import os
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Any
|
|
@@ -149,12 +150,15 @@ class LoginDialog(QDialog, Ui_irodsLogin):
|
|
|
149
150
|
# Resolve password
|
|
150
151
|
typed_pw = self.password_field.text()
|
|
151
152
|
password = self._resolve_password(entry, typed_pw)
|
|
152
|
-
if password is None:
|
|
153
|
-
return
|
|
154
153
|
|
|
155
154
|
# Validate environment config
|
|
156
155
|
if not self._validate_env_config(env_path):
|
|
157
156
|
return
|
|
157
|
+
with open(env_path, encoding="utf-8") as f:
|
|
158
|
+
env_info = json.load(f)
|
|
159
|
+
is_anonymous = env_info.get("irods_user_name", "").lower() == "anonymous"
|
|
160
|
+
if password is None and not is_anonymous:
|
|
161
|
+
return
|
|
158
162
|
|
|
159
163
|
try:
|
|
160
164
|
session = self._create_session(env_path, typed_pw, password)
|
|
@@ -4,7 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
import logging
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from PySide6.
|
|
7
|
+
from PySide6.QtCore import QUrl
|
|
8
|
+
from PySide6.QtGui import QAction, QDesktopServices
|
|
8
9
|
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox
|
|
9
10
|
|
|
10
11
|
from ibridgesgui.gui_utils import UI_FILE_DIR, load_ui
|
|
@@ -13,10 +14,12 @@ from ibridgesgui.mainmenu import (
|
|
|
13
14
|
SessionManager,
|
|
14
15
|
TabManager,
|
|
15
16
|
)
|
|
16
|
-
from ibridgesgui.popup_widgets import CheckConfig
|
|
17
|
+
from ibridgesgui.popup_widgets import CheckConfig, SupplyTicket, TicketEditor
|
|
17
18
|
from ibridgesgui.ui_files.MainMenu import Ui_MainWindow
|
|
18
19
|
from ibridgesgui.welcome import Welcome
|
|
19
20
|
|
|
21
|
+
IBRIDGES_DOCS_URL = "https://ibridges.readthedocs.io"
|
|
22
|
+
GUI_DOCS_URL = "https://ibridges-for-irods.github.io/iBridges-GUI"
|
|
20
23
|
|
|
21
24
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
|
22
25
|
"""Main application window."""
|
|
@@ -43,8 +46,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
43
46
|
self.action_connect.triggered.connect(self._on_connect)
|
|
44
47
|
self.action_close_session.triggered.connect(self._on_disconnect)
|
|
45
48
|
self.action_exit.triggered.connect(self._on_exit)
|
|
46
|
-
self.
|
|
47
|
-
self.
|
|
49
|
+
self.action_edit_configuration.triggered.connect(self._on_edit_env)
|
|
50
|
+
self.action_supply_ticket.triggered.connect(self._on_supply_ticket)
|
|
51
|
+
self.action_create_ticket.triggered.connect(self._on_edit_ticket)
|
|
52
|
+
self.action_gui_docs.triggered.connect(self._on_gui_docs)
|
|
53
|
+
self.action_ibridges_docs.triggered.connect(self._on_ibridges_docs)
|
|
48
54
|
|
|
49
55
|
if session is not None:
|
|
50
56
|
self.on_session_changed(session)
|
|
@@ -88,11 +94,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
88
94
|
|
|
89
95
|
self.tab_manager.update_plugin_menu()
|
|
90
96
|
|
|
97
|
+
def _open_web_page(self, url):
|
|
98
|
+
if not QDesktopServices.openUrl(QUrl(url)):
|
|
99
|
+
self.logger.error("Could not open %s in a web browser.", url)
|
|
100
|
+
|
|
101
|
+
def _on_gui_docs(self) -> None:
|
|
102
|
+
self._open_web_page(GUI_DOCS_URL)
|
|
103
|
+
def _on_ibridges_docs(self) -> None:
|
|
104
|
+
self._open_web_page(IBRIDGES_DOCS_URL)
|
|
105
|
+
|
|
91
106
|
def _on_connect(self) -> None:
|
|
92
107
|
if self.session_manager.session is not None:
|
|
93
108
|
QMessageBox.information(self, "Already connected", "You are already logged in.")
|
|
94
109
|
return
|
|
95
|
-
self.session_manager.
|
|
110
|
+
self.session_manager.login(self)
|
|
96
111
|
|
|
97
112
|
def _on_disconnect(self) -> None:
|
|
98
113
|
self.session_manager.disconnect()
|
|
@@ -101,14 +116,19 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
101
116
|
#self.close()
|
|
102
117
|
QApplication.quit()
|
|
103
118
|
|
|
104
|
-
def
|
|
119
|
+
def _on_edit_env(self) -> None:
|
|
105
120
|
widget = CheckConfig(self.logger, Path("~/.irods").expanduser())
|
|
106
121
|
widget.exec()
|
|
107
122
|
|
|
108
|
-
def
|
|
109
|
-
widget =
|
|
123
|
+
def _on_edit_ticket(self) -> None:
|
|
124
|
+
widget = TicketEditor(self.session_manager.session, self.logger)
|
|
110
125
|
widget.exec()
|
|
111
126
|
|
|
127
|
+
def _on_supply_ticket(self) -> None:
|
|
128
|
+
widget = SupplyTicket(self.session_manager, self.logger)
|
|
129
|
+
widget.exec()
|
|
130
|
+
|
|
131
|
+
|
|
112
132
|
def on_session_changed(self, session) -> None:
|
|
113
133
|
"""Reset when session changes."""
|
|
114
134
|
self.tab_widget.clear()
|
|
@@ -116,10 +136,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
116
136
|
|
|
117
137
|
if session is None:
|
|
118
138
|
self.menuPlugins.setEnabled(False)
|
|
139
|
+
self.action_supply_ticket.setEnabled(False)
|
|
140
|
+
self.action_delete_ticket.setEnabled(False)
|
|
141
|
+
self.action_create_ticket.setEnabled(False)
|
|
119
142
|
self._show_welcome_tab()
|
|
120
143
|
return
|
|
121
144
|
|
|
122
145
|
self.menuPlugins.setEnabled(True)
|
|
146
|
+
self.action_supply_ticket.setEnabled(True)
|
|
147
|
+
if session.irods_session.username != "anonymous":
|
|
148
|
+
self.action_delete_ticket.setEnabled(True)
|
|
149
|
+
self.action_create_ticket.setEnabled(True)
|
|
123
150
|
self.tab_manager.restore_tabs(session, self.app_name, self.logger)
|
|
124
151
|
|
|
125
152
|
def _show_welcome_tab(self) -> None:
|
|
@@ -21,7 +21,7 @@ class SessionManager(QObject):
|
|
|
21
21
|
self.logger = logger
|
|
22
22
|
self.session: Session | None = None
|
|
23
23
|
|
|
24
|
-
def
|
|
24
|
+
def login(self, parent=None):
|
|
25
25
|
"""Get session from Login."""
|
|
26
26
|
dialog = LoginDialog(parent, self.logger, self)
|
|
27
27
|
if dialog.exec() != QDialog.Accepted:
|
|
@@ -94,7 +94,6 @@ class TabManager:
|
|
|
94
94
|
for name in ordered_standard + third_party:
|
|
95
95
|
self.load_tab(name, session, app_name, logger)
|
|
96
96
|
|
|
97
|
-
|
|
98
97
|
def update_plugin_menu(self):
|
|
99
98
|
"""Check and uncheck tab names in main menu."""
|
|
100
99
|
for name, action in self.main_window.plugin_actions.items():
|