diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
index 2b231a81..04aef733 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -27,12 +27,12 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
- python-version: ["3.9", "3.10", "3.11", "3.12"]
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
include:
- os: windows-latest
- python-version: "3.11"
+ python-version: "3.12"
- os: macos-latest
- python-version: "3.11"
+ python-version: "3.12"
runs-on: ${{ matrix.os }}
steps:
diff --git a/ibridgesgui/search.py b/ibridgesgui/search.py
index 6f574267..5e28b69e 100644
--- a/ibridgesgui/search.py
+++ b/ibridgesgui/search.py
@@ -87,22 +87,24 @@ def search(self):
self.error_label.clear()
self.current_batch_num = 0
self.results = None
+ case_sensitive = self.case_sensitive_box.isChecked()
msg, search_path, path_pattern, meta_searches, checksum = self._validate_search_params()
self.logger.debug(
- "Search parameters %s, %s, %s, %s, %s",
+ "Search parameters %s, %s, %s, %s, %s, %s",
msg,
str(search_path),
path_pattern,
str(meta_searches),
checksum,
+ str(case_sensitive),
)
if msg is not None:
self.error_label.setText(msg)
self.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.ArrowCursor))
return
- self._start_search(search_path, path_pattern, meta_searches, checksum)
+ self._start_search(search_path, path_pattern, meta_searches, checksum, case_sensitive)
def next_batch(self):
"""Load next batch of results."""
@@ -283,7 +285,7 @@ def _download_fetch_result(self, thread: dict):
self.error_label.setText("Errors occurred during download. Consult the logs.")
self.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.ArrowCursor))
- def _start_search(self, search_path, path_pattern, meta_searches, checksum):
+ def _start_search(self, search_path, path_pattern, meta_searches, checksum, case_sensitive):
self.search_button.setEnabled(False)
# check if session comes from env file in ibridges config
if is_session_from_config(self.session):
@@ -296,7 +298,13 @@ def _start_search(self, search_path, path_pattern, meta_searches, checksum):
self.error_label.setText("Searching ...")
try:
self.search_thread = SearchThread(
- self.logger, env_path, search_path, path_pattern, meta_searches, checksum
+ self.logger,
+ env_path,
+ search_path,
+ path_pattern,
+ meta_searches,
+ checksum,
+ case_sensitive,
)
except Exception:
self.error_label.setText(
diff --git a/ibridgesgui/threads.py b/ibridgesgui/threads.py
index 37a6b724..1b7d7dbe 100644
--- a/ibridgesgui/threads.py
+++ b/ibridgesgui/threads.py
@@ -13,8 +13,16 @@ class SearchThread(QThread):
result = pyqtSignal(dict)
- def __init__(self, logger, ienv_path: Path, search_path: IrodsPath, path_pattern: str,
- meta_searches: list, checksum: str):
+ def __init__(
+ self,
+ logger,
+ ienv_path: Path,
+ search_path: IrodsPath,
+ path_pattern: str,
+ meta_searches: list,
+ checksum: str,
+ case_sensitive: bool,
+ ):
"""Pass searh parameters."""
super().__init__()
self.logger = logger
@@ -24,6 +32,7 @@ def __init__(self, logger, ienv_path: Path, search_path: IrodsPath, path_pattern
self.search_path = search_path
self.path_pattern = path_pattern
self.checksum = checksum
+ self.case_sensitive = case_sensitive
self.ms = meta_searches
def _delete_session(self):
@@ -38,8 +47,12 @@ def run(self):
search_out = {}
try:
res = search_data(
- self.thread_session, path=self.search_path, path_pattern = self.path_pattern,
- checksum=self.checksum, metadata=self.ms
+ self.thread_session,
+ path=self.search_path,
+ path_pattern=self.path_pattern,
+ checksum=self.checksum,
+ metadata=self.ms,
+ case_sensitive=self.case_sensitive,
)
# convert IrodsPaths to strings, the session will be destroyed at the end of the thread
search_out["results"] = [str(ipath) for ipath in res]
@@ -49,6 +62,7 @@ def run(self):
search_out["error"] = "Search takes too long. Please provide more parameters."
self.result.emit(search_out)
+
class TransferDataThread(QThread):
"""Transfer data between local and iRODS."""
@@ -128,12 +142,9 @@ def run(self):
transfer_out["error"]
+ f"\nTransfer failed, cannot upload {str(local_path)}: {repr(error)}"
)
- self.current_progress.emit([self.up_sizes,
- transferred_size,
- obj_count,
- len(self.ops.upload),
- obj_failed])
-
+ self.current_progress.emit(
+ [self.up_sizes, transferred_size, obj_count, len(self.ops.upload), obj_failed]
+ )
transferred_size = 0
for irods_path, local_path in self.ops.download:
@@ -166,11 +177,9 @@ def run(self):
transfer_out["error"]
+ f"\nTransfer failed, cannot download {str(irods_path)}: {repr(error)}"
)
- self.current_progress.emit([self.down_sizes,
- transferred_size,
- file_count,
- len(self.ops.download),
- file_failed])
+ self.current_progress.emit(
+ [self.down_sizes, transferred_size, file_count, len(self.ops.download), file_failed]
+ )
self.ops.execute_meta_download()
self._delete_session()
diff --git a/ibridgesgui/ui_files/tabBrowser.py b/ibridgesgui/ui_files/tabBrowser.py
index fcbfc2ef..e7861fde 100644
--- a/ibridgesgui/ui_files/tabBrowser.py
+++ b/ibridgesgui/ui_files/tabBrowser.py
@@ -405,7 +405,7 @@ def retranslateUi(self, tabBrowser):
self.delete_meta_button.setText(_translate("tabBrowser", "Delete"))
self.label_4.setText(_translate("tabBrowser", "Key"))
self.label_3.setText(_translate("tabBrowser", "Edit"))
- self.update_meta_button.setText(_translate("tabBrowser", "Set"))
+ self.update_meta_button.setText(_translate("tabBrowser", "Set all keys .."))
self.label_6.setText(_translate("tabBrowser", "Value"))
self.info_tabs.setTabText(self.info_tabs.indexOf(self.metadata), _translate("tabBrowser", "Metadata"))
self.info_tabs.setTabText(self.info_tabs.indexOf(self.preview), _translate("tabBrowser", "Preview"))
diff --git a/ibridgesgui/ui_files/tabBrowser.ui b/ibridgesgui/ui_files/tabBrowser.ui
index 87799a00..052b0465 100644
--- a/ibridgesgui/ui_files/tabBrowser.ui
+++ b/ibridgesgui/ui_files/tabBrowser.ui
@@ -444,7 +444,7 @@ QTabWidget#info_tabs
- Set
+ Set all keys ..
diff --git a/ibridgesgui/ui_files/tabSearch.py b/ibridgesgui/ui_files/tabSearch.py
index 116d733d..a9a9d419 100644
--- a/ibridgesgui/ui_files/tabSearch.py
+++ b/ibridgesgui/ui_files/tabSearch.py
@@ -138,6 +138,11 @@ def setupUi(self, tabSearch):
self.verticalLayout.addLayout(self.gridLayout)
spacerItem3 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed)
self.verticalLayout.addItem(spacerItem3)
+ self.case_sensitive_box = QtWidgets.QCheckBox(parent=tabSearch)
+ self.case_sensitive_box.setObjectName("case_sensitive_box")
+ self.verticalLayout.addWidget(self.case_sensitive_box)
+ spacerItem4 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed)
+ self.verticalLayout.addItem(spacerItem4)
self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.search_button = QtWidgets.QPushButton(parent=tabSearch)
@@ -162,8 +167,8 @@ def setupUi(self, tabSearch):
self.load_more_button = QtWidgets.QPushButton(parent=tabSearch)
self.load_more_button.setObjectName("load_more_button")
self.horizontalLayout_5.addWidget(self.load_more_button)
- spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
- self.horizontalLayout_5.addItem(spacerItem4)
+ spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
+ self.horizontalLayout_5.addItem(spacerItem5)
self.download_button = QtWidgets.QPushButton(parent=tabSearch)
font = QtGui.QFont()
font.setPointSize(16)
@@ -203,11 +208,11 @@ def setupUi(self, tabSearch):
self.search_table.setHorizontalHeaderItem(4, item)
self.search_table.horizontalHeader().setStretchLastSection(True)
self.verticalLayout.addWidget(self.search_table)
- spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
- self.verticalLayout.addItem(spacerItem5)
- self.verticalLayout_3.addLayout(self.verticalLayout)
spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
- self.verticalLayout_3.addItem(spacerItem6)
+ self.verticalLayout.addItem(spacerItem6)
+ self.verticalLayout_3.addLayout(self.verticalLayout)
+ spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
+ self.verticalLayout_3.addItem(spacerItem7)
self.retranslateUi(tabSearch)
QtCore.QMetaObject.connectSlotsByName(tabSearch)
@@ -223,6 +228,7 @@ def retranslateUi(self, tabSearch):
self.label1.setText(_translate("tabSearch", "Search by Metadata"))
self.label_7.setText(_translate("tabSearch", "Units"))
self.label_4.setText(_translate("tabSearch", "Key"))
+ self.case_sensitive_box.setText(_translate("tabSearch", "Case sensitive"))
self.search_button.setText(_translate("tabSearch", "Search"))
self.clear_button.setText(_translate("tabSearch", "Clear Results"))
self.load_more_button.setText(_translate("tabSearch", "Next 25"))
diff --git a/ibridgesgui/ui_files/tabSearch.ui b/ibridgesgui/ui_files/tabSearch.ui
index 4b7244c6..7e9c3300 100644
--- a/ibridgesgui/ui_files/tabSearch.ui
+++ b/ibridgesgui/ui_files/tabSearch.ui
@@ -236,6 +236,29 @@ QTabWidget#info_tabs
+ -
+
+
+ Case sensitive
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 20
+
+
+
+
-
-
diff --git a/pyproject.toml b/pyproject.toml
index 95f46838..da35c274 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,12 +17,13 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
"Development Status :: 3 - Alpha",
]
dependencies = [
"PyQt6>=6.4.2",
- "ibridges==1.1.1",
+ "ibridges>=1.2.0, < 1.3",
"setproctitle==1.3.3",
"importlib-resources;python_version<='3.10'",
]