ibridgesgui 0.2.0__tar.gz → 0.2.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 (105) hide show
  1. ibridgesgui-0.2.2/.github/workflows/pypi_release.yml +39 -0
  2. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/PKG-INFO +15 -9
  3. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/README.md +13 -7
  4. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/docs/getting-started.qmd +4 -5
  5. ibridgesgui-0.2.2/docs/screenshots/add_check_config.png +0 -0
  6. ibridgesgui-0.2.2/docs/screenshots/add_new_config.png +0 -0
  7. ibridgesgui-0.2.2/docs/screenshots/browser.png +0 -0
  8. ibridgesgui-0.2.2/docs/screenshots/delete.png +0 -0
  9. ibridgesgui-0.2.2/docs/screenshots/login.png +0 -0
  10. ibridgesgui-0.2.2/docs/screenshots/metadata.png +0 -0
  11. ibridgesgui-0.2.2/docs/screenshots/permissions.png +0 -0
  12. ibridgesgui-0.2.2/docs/screenshots/replicas.png +0 -0
  13. ibridgesgui-0.2.2/docs/screenshots/search.png +0 -0
  14. ibridgesgui-0.2.2/docs/screenshots/start_ibridges.png +0 -0
  15. ibridgesgui-0.2.2/docs/screenshots/sync.png +0 -0
  16. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/docs/userdoc.qmd +1 -1
  17. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/__main__.py +31 -12
  18. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/_version.py +2 -2
  19. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/browser.py +3 -9
  20. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/gui_utils.py +3 -0
  21. ibridgesgui-0.2.2/ibridgesgui/icons/logo.png +0 -0
  22. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/irods_tree_model.py +4 -1
  23. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/login.py +35 -3
  24. ibridgesgui-0.2.2/ibridgesgui/logviewer.py +50 -0
  25. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/popup_widgets.py +29 -0
  26. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/search.py +2 -1
  27. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/sync.py +7 -3
  28. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/threads.py +2 -0
  29. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/MainMenu.py +17 -13
  30. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/MainMenu.ui +29 -12
  31. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/configCheck.py +47 -30
  32. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/configCheck.ui +38 -9
  33. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/createCollection.py +42 -36
  34. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/createCollection.ui +25 -28
  35. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/irodsLogin.py +41 -52
  36. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/irodsLogin.ui +25 -17
  37. ibridgesgui-0.2.2/ibridgesgui/ui_files/renameItem.py +94 -0
  38. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/renameItem.ui +24 -7
  39. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabBrowser.py +104 -145
  40. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabBrowser.ui +53 -46
  41. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabInfo.py +40 -57
  42. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabInfo.ui +15 -10
  43. ibridgesgui-0.2.2/ibridgesgui/ui_files/tabLogging.py +49 -0
  44. ibridgesgui-0.2.2/ibridgesgui/ui_files/tabLogging.ui +51 -0
  45. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabSearch.py +46 -67
  46. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabSearch.ui +18 -17
  47. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabSync.py +30 -15
  48. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/tabSync.ui +29 -14
  49. ibridgesgui-0.2.2/ibridgesgui/ui_files/welcome.py +48 -0
  50. ibridgesgui-0.2.2/ibridgesgui/ui_files/welcome.ui +47 -0
  51. ibridgesgui-0.2.2/ibridgesgui/welcome.py +43 -0
  52. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui.egg-info/PKG-INFO +15 -9
  53. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui.egg-info/SOURCES.txt +8 -4
  54. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui.egg-info/requires.txt +1 -1
  55. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/pyproject.toml +2 -3
  56. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/requirements.txt +1 -1
  57. ibridgesgui-0.2.2/requirements_dev.txt +4 -0
  58. ibridgesgui-0.2.0/docs/screenshots/add_check_config.png +0 -0
  59. ibridgesgui-0.2.0/docs/screenshots/add_new_config.png +0 -0
  60. ibridgesgui-0.2.0/docs/screenshots/browser.png +0 -0
  61. ibridgesgui-0.2.0/docs/screenshots/delete.png +0 -0
  62. ibridgesgui-0.2.0/docs/screenshots/login.png +0 -0
  63. ibridgesgui-0.2.0/docs/screenshots/metadata.png +0 -0
  64. ibridgesgui-0.2.0/docs/screenshots/permissions.png +0 -0
  65. ibridgesgui-0.2.0/docs/screenshots/replicas.png +0 -0
  66. ibridgesgui-0.2.0/docs/screenshots/search.png +0 -0
  67. ibridgesgui-0.2.0/docs/screenshots/start_ibridges.png +0 -0
  68. ibridgesgui-0.2.0/docs/screenshots/sync.png +0 -0
  69. ibridgesgui-0.2.0/ibridgesgui/ui_files/dataTransferState.py +0 -114
  70. ibridgesgui-0.2.0/ibridgesgui/ui_files/dataTransferState.ui +0 -187
  71. ibridgesgui-0.2.0/ibridgesgui/ui_files/renameItem.py +0 -77
  72. ibridgesgui-0.2.0/ibridgesgui/ui_files/tabUpDownload.py +0 -303
  73. ibridgesgui-0.2.0/ibridgesgui/ui_files/tabUpDownload.ui +0 -460
  74. ibridgesgui-0.2.0/requirements_dev.txt +0 -4
  75. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/.flake8 +0 -0
  76. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  77. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  78. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/.github/dependabot.yml +0 -0
  79. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/.github/workflows/linter.yml +0 -0
  80. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/.github/workflows/publish.yml +0 -0
  81. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/.gitignore +0 -0
  82. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/LICENSE +0 -0
  83. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/_quarto.yml +0 -0
  84. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/docs/info.qmd +0 -0
  85. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/__init__.py +0 -0
  86. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/config.py +0 -0
  87. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/arrow-left.png +0 -0
  88. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/arrow-right.png +0 -0
  89. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/arrow-up.png +0 -0
  90. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/home.png +0 -0
  91. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/iBridges.ico +0 -0
  92. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/loading_circle.gif +0 -0
  93. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/nosync.png +0 -0
  94. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/refresh.png +0 -0
  95. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/icons/syncing.png +0 -0
  96. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/info.py +0 -0
  97. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/.py +0 -0
  98. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/ExampleTab.py +0 -0
  99. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/ExampleTab.ui +0 -0
  100. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui/ui_files/__init__.py +0 -0
  101. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui.egg-info/dependency_links.txt +0 -0
  102. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui.egg-info/entry_points.txt +0 -0
  103. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/ibridgesgui.egg-info/top_level.txt +0 -0
  104. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/index.qmd +0 -0
  105. {ibridgesgui-0.2.0 → ibridgesgui-0.2.2}/setup.cfg +0 -0
@@ -0,0 +1,39 @@
1
+ # This workflow will upload a Python Package using Twine when a release is created
2
+ # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3
+
4
+ # This workflow uses actions that are not certified by GitHub.
5
+ # They are provided by a third-party and are governed by
6
+ # separate terms of service, privacy policy, and support
7
+ # documentation.
8
+
9
+ name: Upload Python Package
10
+
11
+ on:
12
+ release:
13
+ types: [published]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ deploy:
20
+
21
+ runs-on: ubuntu-latest
22
+
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v4
27
+ with:
28
+ python-version: '3.x'
29
+ - name: Install dependencies
30
+ run: |
31
+ python -m pip install --upgrade pip
32
+ pip install build
33
+ - name: Build package
34
+ run: python -m build
35
+ - name: Publish package
36
+ uses: pypa/gh-action-pypi-publish@7eb3b701d11256e583f5b49899c5e7203deab573
37
+ with:
38
+ user: __token__
39
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ibridgesgui
3
- Version: 0.2.0
3
+ Version: 0.2.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 GENERAL PUBLIC LICENSE
@@ -690,7 +690,7 @@ Requires-Python: >=3.9
690
690
  Description-Content-Type: text/markdown
691
691
  License-File: LICENSE
692
692
  Requires-Dist: PyQt6>=6.4.2
693
- Requires-Dist: ibridges==0.2.1
693
+ Requires-Dist: ibridges==0.2.2
694
694
  Requires-Dist: setproctitle==1.3.3
695
695
  Requires-Dist: importlib-resources; python_version <= "3.10"
696
696
  Provides-Extra: test
@@ -700,10 +700,7 @@ Requires-Dist: pytest-cov; extra == "test"
700
700
  Requires-Dist: ruff; extra == "test"
701
701
  Requires-Dist: mypy; extra == "test"
702
702
 
703
- # iBridges
704
-
705
- [![Python package](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml)
706
- [![Quarto Publish](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml)
703
+ # iBridges <img src="ibridgesgui/icons/logo.png" width="150" align="right">
707
704
 
708
705
  <p align="center">
709
706
  <p align="center">
@@ -713,6 +710,9 @@ Requires-Dist: mypy; extra == "test"
713
710
  </p>
714
711
  </p>
715
712
 
713
+ [![Python package](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml)
714
+ [![Quarto Publish](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml)
715
+
716
716
  ## About
717
717
 
718
718
  The git repository contains a generic *iRODS* graphical user interface. The iRODS functionality is based on [ibridges](https://github.com/UtrechtUniversity/iBridges) and works with any *iRODS* instance.
@@ -732,12 +732,18 @@ The git repository contains a generic *iRODS* graphical user interface. The iRO
732
732
  - Safe default options when working with your data.
733
733
 
734
734
  ## Installation
735
- - As python package
735
+ - The python package
736
736
 
737
737
  ```bash
738
- pip install ibridges
739
- pip install git+https://github.com/chStaiger/iBridges-Gui.git@main
738
+ pip install ibridgesgui
740
739
  ```
740
+
741
+ - A specific branch of the git repository (testers, developers)
742
+
743
+ ```bash
744
+ pip install git+https://github.com/chStaiger/iBridges-Gui.git@branch-name
745
+ ```
746
+
741
747
  - Locally from code (for developers)
742
748
 
743
749
  ```bash
@@ -1,7 +1,4 @@
1
- # iBridges
2
-
3
- [![Python package](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml)
4
- [![Quarto Publish](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml)
1
+ # iBridges <img src="ibridgesgui/icons/logo.png" width="150" align="right">
5
2
 
6
3
  <p align="center">
7
4
  <p align="center">
@@ -11,6 +8,9 @@
11
8
  </p>
12
9
  </p>
13
10
 
11
+ [![Python package](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml)
12
+ [![Quarto Publish](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml)
13
+
14
14
  ## About
15
15
 
16
16
  The git repository contains a generic *iRODS* graphical user interface. The iRODS functionality is based on [ibridges](https://github.com/UtrechtUniversity/iBridges) and works with any *iRODS* instance.
@@ -30,12 +30,18 @@ The git repository contains a generic *iRODS* graphical user interface. The iRO
30
30
  - Safe default options when working with your data.
31
31
 
32
32
  ## Installation
33
- - As python package
33
+ - The python package
34
34
 
35
35
  ```bash
36
- pip install ibridges
37
- pip install git+https://github.com/chStaiger/iBridges-Gui.git@main
36
+ pip install ibridgesgui
38
37
  ```
38
+
39
+ - A specific branch of the git repository (testers, developers)
40
+
41
+ ```bash
42
+ pip install git+https://github.com/chStaiger/iBridges-Gui.git@branch-name
43
+ ```
44
+
39
45
  - Locally from code (for developers)
40
46
 
41
47
  ```bash
@@ -4,8 +4,7 @@
4
4
  ## Install iBridges-gui
5
5
 
6
6
  ```
7
- pip install ibridges==0.1.6
8
- pip install pip install git+https://github.com/chStaiger/iBridges-Gui.git@main
7
+ pip install ibridgesgui
9
8
  ```
10
9
 
11
10
  ## Start the program
@@ -20,7 +19,7 @@ When you start iBridges you will find in the upper drop-down menu `Connect` and
20
19
 
21
20
  Before we start iBridges for the first time let us check and create a valid configuration file; Click on `Configure -> Add Configuration`.
22
21
 
23
- ![](screenshots/add_check_config.png){width=50% fig-align="center"}
22
+ ![](screenshots/add_check_config.png){width=75% fig-align="center"}
24
23
 
25
24
  If you already stored an `irods_environment.json` in the directory `~/.irods` (Windows `C:\Users\<username>\.irods`) then you can use the drop-down menu (little downward arrow) to load the configuration.
26
25
 
@@ -28,7 +27,7 @@ If you do not have an iRODS configuration yet, click on the button `New Config`.
28
27
 
29
28
  You should see now something like:
30
29
 
31
- ![](screenshots/add_new_config.png){width=50% fig-align="center"}
30
+ ![](screenshots/add_new_config.png){width=75% fig-align="center"}
32
31
 
33
32
  Click the `Check` button to find out whether you can make a connection to the iRODS server. If you get error messages, adjust the contents accordingly. If you are unsure, please contact your iRODS service provider.
34
33
 
@@ -38,7 +37,7 @@ Once the check runs through fine click the `Save` or `Save as` button. This will
38
37
 
39
38
  To start working with data in iRODS select `Connect -> Connect to iRODS`. This will open the pop-up window
40
39
 
41
- ![](screenshots/login.png){width=50% fig-align="center"}
40
+ ![](screenshots/login.png){width=75% fig-align="center"}
42
41
 
43
42
  Select the file we edited above from the drop-down menu and provide your password. If you already logged into iRODS with this or another iRODS client your password might still be cached. This is indicated by the `******`. Hit `<Enter>` or press the `Connect` button.
44
43
 
@@ -110,7 +110,7 @@ In the case you want to download a large collection from iRODS, create the desti
110
110
 
111
111
  **Note that the wild card is `%`**!
112
112
 
113
- In the example search we are looking for data in a collection called *my_books* which can be located anywhere in the iRODS tree. This is denoted by `%/` and we specify that we want to retrieve data which carries the metadata key *author* where the value ends with *Carroll*.
113
+ In the example search we are looking for data in a collection called *my_books* which can be located anywhere in the iRODS tree. This is denoted by `%` and we specify that we want to retrieve data which carries the metadata key *author* where the value ends with *Carroll*.
114
114
 
115
115
  *Single click* on items in the list and click the `Download` button to open a dialog to select the destination for the data and start the download
116
116
 
@@ -5,19 +5,21 @@ import logging
5
5
  import sys
6
6
  from pathlib import Path
7
7
 
8
+ import PyQt6.QtGui
8
9
  import PyQt6.QtWidgets
9
10
  import PyQt6.uic
10
11
  import setproctitle
11
12
 
12
13
  from ibridgesgui.browser import Browser
13
14
  from ibridgesgui.config import get_log_level, init_logger, set_log_level
14
- from ibridgesgui.gui_utils import UI_FILE_DIR
15
15
  from ibridgesgui.info import Info
16
16
  from ibridgesgui.login import Login
17
+ from ibridgesgui.logviewer import LogViewer
17
18
  from ibridgesgui.popup_widgets import CheckConfig
18
19
  from ibridgesgui.search import Search
19
20
  from ibridgesgui.sync import Sync
20
21
  from ibridgesgui.ui_files.MainMenu import Ui_MainWindow
22
+ from ibridgesgui.welcome import Welcome
21
23
 
22
24
  # Global constants
23
25
  THIS_APPLICATION = "ibridges-gui"
@@ -32,24 +34,21 @@ class MainMenu(PyQt6.QtWidgets.QMainWindow, Ui_MainWindow):
32
34
  def __init__(self, app_name):
33
35
  """Initialise the main window."""
34
36
  super().__init__()
35
- if getattr(sys, "frozen", False):
36
- super().setupUi(self)
37
- else:
38
- PyQt6.uic.loadUi(UI_FILE_DIR / "MainMenu.ui", self)
37
+ super().setupUi(self)
38
+
39
+ app.aboutToQuit.connect(self.close_event)
39
40
 
40
41
  self.logger = logging.getLogger(app_name)
42
+
41
43
  self.irods_path = Path("~", ".irods").expanduser()
42
44
  self.app_name = app_name
45
+ self.welcome_tab()
43
46
  self.ui_tabs_lookup = {
44
47
  "tabBrowser": self.init_browser_tab,
45
48
  "tabSync": self.init_sync_tab,
46
49
  "tabSearch": self.init_search_tab,
47
- #'tabDataBundle': self.setupTabDataBundle,
48
- #'tabCreateTicket': self.setupTabCreateTicket,
49
- #'tabELNData': self.setupTabELNData,
50
- #'tabAmberWorkflow': self.setupTabAmberWorkflow,
51
50
  "tabInfo": self.init_info_tab,
52
- #'tabExample': self.setupTabExample,
51
+ "tabLog": self.init_log_tab
53
52
  }
54
53
 
55
54
  self.session = None
@@ -62,6 +61,7 @@ class MainMenu(PyQt6.QtWidgets.QMainWindow, Ui_MainWindow):
62
61
  self.action_check_configuration.triggered.connect(self.inspect_env_file)
63
62
  self.tab_widget.setCurrentIndex(0)
64
63
 
64
+
65
65
  def disconnect(self):
66
66
  """Close iRODS session."""
67
67
  self.error_label.clear()
@@ -71,6 +71,7 @@ class MainMenu(PyQt6.QtWidgets.QMainWindow, Ui_MainWindow):
71
71
  self.session = None
72
72
  self.session_dict.clear()
73
73
  self.tab_widget.clear()
74
+ self.welcome_tab()
74
75
 
75
76
  def connect(self):
76
77
  """Create iRODS session."""
@@ -82,6 +83,7 @@ class MainMenu(PyQt6.QtWidgets.QMainWindow, Ui_MainWindow):
82
83
  login_window = Login(self.session_dict, self.app_name)
83
84
  login_window.exec()
84
85
  # Trick to get the session object from the QDialog
86
+
85
87
  if "session" in self.session_dict:
86
88
  self.session = self.session_dict["session"]
87
89
  try:
@@ -89,6 +91,8 @@ class MainMenu(PyQt6.QtWidgets.QMainWindow, Ui_MainWindow):
89
91
  except:
90
92
  self.session = None
91
93
  raise
94
+ else:
95
+ self.logger.exception("No session created. %s", self.session_dict)
92
96
 
93
97
  def exit(self):
94
98
  """Quit program."""
@@ -106,17 +110,32 @@ class MainMenu(PyQt6.QtWidgets.QMainWindow, Ui_MainWindow):
106
110
  else:
107
111
  pass
108
112
 
113
+ def close_event(self):
114
+ """Close program properly if main window is closed."""
115
+ self.disconnect()
116
+ sys.exit()
117
+
109
118
  def setup_tabs(self):
110
119
  """Init tab view."""
111
120
  for tab_fun in self.ui_tabs_lookup.values():
112
121
  tab_fun()
113
122
  # self.ui_tabs_lookup[tab_name]()
114
123
 
124
+ def welcome_tab(self):
125
+ """Create first tab."""
126
+ welcome = Welcome()
127
+ self.tab_widget.addTab(welcome, "iBridges")
128
+
115
129
  def init_info_tab(self):
116
130
  """Create info."""
117
131
  irods_info = Info(self.session)
118
132
  self.tab_widget.addTab(irods_info, "Info")
119
133
 
134
+ def init_log_tab(self):
135
+ """Create log tab."""
136
+ ibridges_log = LogViewer(self.logger)
137
+ self.tab_widget.addTab(ibridges_log, "Logs")
138
+
120
139
  def init_browser_tab(self):
121
140
  """Create browser."""
122
141
  self.irods_browser = Browser(self.session, self.app_name)
@@ -151,8 +170,8 @@ def main():
151
170
  if log_level is not None:
152
171
  init_logger(THIS_APPLICATION, log_level)
153
172
  else:
154
- set_log_level("error")
155
- init_logger(THIS_APPLICATION, "error")
173
+ set_log_level("debug")
174
+ init_logger(THIS_APPLICATION, "debug")
156
175
  main_widget = PyQt6.QtWidgets.QStackedWidget()
157
176
  main_app = MainMenu(THIS_APPLICATION)
158
177
  main_widget.addWidget(main_app)
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.0.2a1.dev45+gb6f238a.d20240612'
16
- __version_tuple__ = version_tuple = (0, 0, 2, 'dev45', 'gb6f238a.d20240612')
15
+ __version__ = version = '0.2.2'
16
+ __version_tuple__ = version_tuple = (0, 2, 2)
@@ -48,14 +48,7 @@ class Browser(PyQt6.QtWidgets.QWidget, Ui_tabBrowser):
48
48
  self.session, f"/{self.session.zone}/home/{self.session.username}"
49
49
  )
50
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
- )
51
+ self.root_coll = IrodsPath(self.session, root_path).collection
59
52
  self.reset_path()
60
53
  self.browse()
61
54
 
@@ -231,7 +224,6 @@ class Browser(PyQt6.QtWidgets.QWidget, Ui_tabBrowser):
231
224
  len(coll.data_objects) + len(coll.subcollections),
232
225
  coll_data + obj_data,
233
226
  )
234
- self.browser_table.resizeColumnsToContents()
235
227
  except Exception:
236
228
  self.browser_table.setRowCount(0)
237
229
  self.logger.exception("Cannot load browser.")
@@ -523,6 +515,8 @@ class Browser(PyQt6.QtWidgets.QWidget, Ui_tabBrowser):
523
515
  ]
524
516
  else:
525
517
  content = [f"No Preview for: {irods_path}"]
518
+ else:
519
+ content = [f"No Preview for: {irods_path}"]
526
520
  populate_textfield(self.preview_browser, content)
527
521
 
528
522
  def _get_item_path(self, row):
@@ -16,12 +16,15 @@ except ImportError:
16
16
 
17
17
 
18
18
  UI_FILE_DIR = files(__package__) / "ui_files"
19
+ LOGO_DIR = files(__package__) / "icons"
19
20
 
20
21
 
21
22
  # Widget utils
22
23
  def populate_table(table_widget, rows: int, data_by_row: list):
23
24
  """Populate a table-like pyqt widget with data."""
25
+ table_widget.setRowCount(0)
24
26
  table_widget.setRowCount(rows)
27
+
25
28
  for row, data in enumerate(data_by_row):
26
29
  for col, item in enumerate(data):
27
30
  table_widget.setItem(row, col, PyQt6.QtWidgets.QTableWidgetItem(str(item)))
@@ -105,9 +105,12 @@ class IrodsTreeModel(PyQt6.QtGui.QStandardItemModel):
105
105
  _, level, _, _, _, abs_irods_path = tree_item_data
106
106
  parent_coll = IrodsPath(self.session, abs_irods_path).collection
107
107
 
108
+ # the irods root also contains the irods root as subcollection
109
+ subcolls = [c for c in parent_coll.subcollections if c.path != "/"]
110
+ dataobjs = parent_coll.data_objects
108
111
  # we assume that tree_item has no children yet.
109
112
  new_nodes = {}
110
- for item in parent_coll.subcollections + parent_coll.data_objects:
113
+ for item in subcolls + dataobjs:
111
114
  row = self._tree_row_from_irods_item(item, parent_coll.id, int(level))
112
115
  tree_item.appendRow(row)
113
116
  new_nodes[item.id] = tree_item.child(tree_item.rowCount() - 1)
@@ -4,8 +4,10 @@ import logging
4
4
  import sys
5
5
  from pathlib import Path
6
6
 
7
- from ibridges import Session
7
+ from ibridges import IrodsPath, Session
8
+ from ibridges.resources import Resources
8
9
  from ibridges.session import LoginError, PasswordError
10
+ from irods.exception import ResourceDoesNotExist
9
11
  from PyQt6.QtWidgets import QDialog, QLineEdit
10
12
  from PyQt6.uic import loadUi
11
13
 
@@ -77,7 +79,6 @@ class Login(QDialog, Ui_irodsLogin):
77
79
  else:
78
80
  session = Session(irods_env=env_file, password=self.password_field.text())
79
81
  self.logger.debug("Login with %s and password from prompt.", env_file)
80
- self.session_dict["session"] = session
81
82
  self.logger.info(
82
83
  "Logged in as %s to %s; working coll %s",
83
84
  session.username,
@@ -85,17 +86,48 @@ class Login(QDialog, Ui_irodsLogin):
85
86
  session.home,
86
87
  )
87
88
  session.write_pam_password()
89
+ self.session_dict["session"] = session
88
90
  set_last_ienv_path(env_file.name)
89
- self.close()
90
91
  except LoginError:
91
92
  self.error_label.setText("irods_environment.json not setup correctly.")
93
+ self.logger.error("irods_environment.json not setup correctly.")
92
94
  except PasswordError:
93
95
  self.error_label.setText("Wrong password!")
96
+ self.logger.error("Wrong password provided.")
94
97
  except ConnectionError:
95
98
  self.error_label.setText(
96
99
  "Cannot connect to server. Check Internet, host name and port."
97
100
  )
101
+ self.logger.exception("Network error.")
98
102
  except Exception as err:
99
103
  log_path = Path("~/.ibridges")
100
104
  self.logger.exception("Failed to login: %s", repr(err))
101
105
  self.error_label.setText(f"Login failed, consult the log file(s) in {log_path}")
106
+
107
+ #check irods_home
108
+ fail_home = True
109
+ if not IrodsPath(self.session_dict["session"]).collection_exists():
110
+ self.error_label.setText(f'"irods_home": "{session.home}" does not exist.')
111
+ self.logger.error("irods_home does not exist.")
112
+ else:
113
+ fail_home = False
114
+
115
+ #check existance of default resource
116
+ fail_resc = True
117
+ try:
118
+ resc = Resources(self.session_dict["session"]).get_resource(session.default_resc)
119
+ if resc.parent is None:
120
+ fail_resc = False
121
+ else:
122
+ self.error_label.setText(f'"default_resource": "{session.default_resc}" not valid.')
123
+ except ResourceDoesNotExist:
124
+ self.error_label.setText(
125
+ f'"default_resource": "{session.default_resc}" does not exist.')
126
+ self.logger.error("Default resource does not exist.")
127
+ except AttributeError:
128
+ self.error_label.setText(f'"default_resource": "{session.default_resc}" not valid.')
129
+
130
+ if fail_resc or fail_home:
131
+ del self.session_dict["session"]
132
+ else:
133
+ self.close()
@@ -0,0 +1,50 @@
1
+ """Logging tab."""
2
+
3
+ import logging
4
+ import sys
5
+
6
+ import PyQt6
7
+
8
+ from ibridgesgui.config import CONFIG_DIR
9
+ from ibridgesgui.gui_utils import UI_FILE_DIR
10
+ from ibridgesgui.ui_files.tabLogging import Ui_tabLogging
11
+
12
+
13
+ class QTextEditLogger(logging.Handler, PyQt6.QtCore.QObject):
14
+ """Logging in a Qt text browser."""
15
+
16
+ append_plain_text = PyQt6.QtCore.pyqtSignal(str)
17
+
18
+ def __init__(self, text_browser):
19
+ """Initialise."""
20
+ super().__init__()
21
+ PyQt6.QtCore.QObject.__init__(self)
22
+ self.widget = text_browser
23
+ self.widget.setReadOnly(True)
24
+ self.append_plain_text.connect(self.widget.insertPlainText)
25
+
26
+ def emit(self, record):
27
+ """Emit when new logging accurs."""
28
+ msg = self.format(record)+"\n"
29
+ self.append_plain_text.emit(msg)
30
+
31
+
32
+ class LogViewer(PyQt6.QtWidgets.QWidget, Ui_tabLogging):
33
+ """Set iBridges logging in GUI."""
34
+
35
+ def __init__(self, logger):
36
+ """Initialise the tab."""
37
+ super().__init__()
38
+ if getattr(sys, "frozen", False):
39
+ super().setupUi(self)
40
+ else:
41
+ PyQt6.uic.loadUi(UI_FILE_DIR / "tabLogging.ui", self)
42
+
43
+ self.logger = logger
44
+ self.log_label.setText(str(CONFIG_DIR))
45
+ self.log_text = QTextEditLogger(self.log_browser)
46
+ self.log_text.setFormatter(
47
+ logging.Formatter(
48
+ '%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s'))
49
+ self.logger.addHandler(self.log_text)
50
+ self.logger.setLevel(logging.DEBUG)
@@ -6,6 +6,7 @@ import sys
6
6
 
7
7
  import irods
8
8
  from ibridges import IrodsPath
9
+ from ibridges.util import find_environment_provider, get_environment_providers
9
10
  from PyQt6 import QtCore
10
11
  from PyQt6.QtWidgets import QDialog, QFileDialog
11
12
  from PyQt6.uic import loadUi
@@ -79,6 +80,8 @@ class CreateDirectory(QDialog, Ui_createCollection):
79
80
  try:
80
81
  os.makedirs(new_dir_path)
81
82
  self.done(1)
83
+ except FileExistsError:
84
+ self.error_label.setText("ERROR: Folder already exists.")
82
85
  except Exception as error:
83
86
  if hasattr(error, "message"):
84
87
  self.error_label.setText(error.message)
@@ -138,8 +141,10 @@ class CheckConfig(QDialog, Ui_configCheck):
138
141
  self.env_path = env_path
139
142
  self.setWindowTitle("Create, edit and inspect iRODS environment")
140
143
  self._init_env_box()
144
+ self._init_template_box()
141
145
 
142
146
  self.envbox.activated.connect(self.load_env)
147
+ self.template_box.activated.connect(self.load_template)
143
148
  self.new_button.clicked.connect(self.create_env)
144
149
  self.check_button.clicked.connect(self.check_env)
145
150
  self.save_button.clicked.connect(self.save_env)
@@ -153,6 +158,30 @@ class CheckConfig(QDialog, Ui_configCheck):
153
158
  self.envbox.addItems(env_jsons)
154
159
  self.envbox.setCurrentIndex(0)
155
160
 
161
+ def _init_template_box(self):
162
+ self.template_box.clear()
163
+ providers = get_environment_providers()
164
+
165
+ if len(providers) == 0:
166
+ self.template_box.hide()
167
+ return
168
+
169
+ templates = [key+": "+descr for p in providers
170
+ for key, descr in p.descriptions.items()]
171
+ if len(templates) > 0:
172
+ self.template_box.addItems(templates)
173
+ self.template_box.setCurrentIndex(0)
174
+
175
+ def load_template(self):
176
+ """Load environment template into text field."""
177
+ self.error_label.clear()
178
+ template = self.template_box.currentText()
179
+ key = template.split(": ")[0]
180
+ env_json = find_environment_provider(get_environment_providers(), key).\
181
+ environment_json(key, "USERNAME").split("\n")
182
+ populate_textfield(self.env_field, env_json)
183
+ self.error_label.setText("Please fill in your user name.")
184
+
156
185
  def load_env(self):
157
186
  """Load json into text field."""
158
187
  self.error_label.clear()
@@ -184,7 +184,8 @@ class Search(PyQt6.QtWidgets.QWidget, Ui_tabSearch):
184
184
  key.text(): "%" if val.text() == "" else val.text()
185
185
  for key, val in zip(self.keys, self.vals)
186
186
  }
187
- del key_vals[""]
187
+ if "" in key_vals:
188
+ del key_vals[""]
188
189
 
189
190
  path = self.path_field.text() if self.path_field.text() != "" else None
190
191
  checksum = self.checksum_field.text() if self.checksum_field.text() != "" else None
@@ -89,7 +89,7 @@ class Sync(PyQt6.QtWidgets.QWidget, Ui_tabSync):
89
89
  def irods_root(self):
90
90
  """Retrieve lowest visible level in the iRODS tree for the user."""
91
91
  lowest = IrodsPath(self.session).absolute()
92
- while lowest.parent.exists():
92
+ while lowest.parent.exists() and str(lowest) != "/":
93
93
  lowest = lowest.parent
94
94
  return lowest
95
95
 
@@ -276,8 +276,12 @@ class Sync(PyQt6.QtWidgets.QWidget, Ui_tabSync):
276
276
  return
277
277
 
278
278
  self.error_label.clear()
279
- table_data = thread_output["result"]["upload"] + thread_output["result"]["download"]
280
-
279
+ table_data = [
280
+ (ipath, lpath, ipath.size) for ipath, lpath in thread_output["result"]["download"]
281
+ ] + [
282
+ (lpath, ipath, lpath.stat().st_size)
283
+ for lpath, ipath in thread_output["result"]["upload"]
284
+ ]
281
285
  populate_table(self.diff_table, len(table_data), table_data)
282
286
  if len(table_data) == 0:
283
287
  self.error_label.setText("Data is already synchronised.")
@@ -89,6 +89,7 @@ class TransferDataThread(QThread):
89
89
  transfer_out = {}
90
90
  transfer_out["error"] = ""
91
91
 
92
+ emit_string = "Create collections."
92
93
  for coll in self.diffs["create_collection"]:
93
94
  try:
94
95
  IrodsPath.create_collection(self.thread_session, coll)
@@ -101,6 +102,7 @@ class TransferDataThread(QThread):
101
102
  transfer_out["error"] + f"\nTransfer failed Cannot create {coll}: {repr(error)}"
102
103
  )
103
104
 
105
+ emit_string = "Create folders."
104
106
  for folder in self.diffs["create_dir"]:
105
107
  print(f"create {folder}")
106
108
  try: