Skip to content

Commit

Permalink
Merge pull request #287 from iBridges-for-iRODS/develop
Browse files Browse the repository at this point in the history
v1.3.0
  • Loading branch information
chStaiger authored Nov 26, 2024
2 parents 3395ded + 67a68bc commit 1d33acd
Show file tree
Hide file tree
Showing 17 changed files with 184 additions and 103 deletions.
19 changes: 14 additions & 5 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,31 @@ on:
push:
branches:
- develop
paths-ignore:
- 'docs/**'
- 'README.md'
- '_quarto.yml'
- 'index.qmd'

pull_request:
branches:
- develop
paths-ignore:
- 'docs/**'
- 'README.md'
- '_quarto.yml'
- 'index.qmd'

jobs:
build:
strategy:
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:
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# iBridges GUI <img src="https://github.com/chStaiger/iBridges-Gui/blob/develop/ibridgesgui/icons/logo.png?raw=true" width="150" align="right">
# iBridges GUI <img src="https://github.com/iBridges-for-iRods/iBridges-GUI/blob/3395dedf2c7b919e108356a3289eb2c2fc66af6d/ibridgesgui/icons/logo.png" width="150" align="right">


<p align="center">
<p align="center">
<a href="https://chstaiger.github.io/iBridges-Gui/"><strong> Documentation »</strong></a> .
<a href="https://github.com/chStaiger/iBridges-Gui/issues">Report Bug or Request Feature</a>
<a href="https://ibridges-for-irods.github.io/iBridges-GUI/"><strong> Documentation »</strong></a> .
<a href="https://github.com/iBridges-for-iRods/iBridges-GUI/issues">Report Bug or Request Feature</a>
.
</p>
</p>

[![Python package](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/linter.yml)
[![Quarto Publish](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml/badge.svg)](https://github.com/chStaiger/iBridges-Gui/actions/workflows/publish.yml)
[![Python package](https://github.com/iBridges-for-iRods/iBridges-GUI/actions/workflows/linter.yml/badge.svg)](https://github.com/iBridges-for-iRods/iBridges-GUI/actions/workflows/linter.yml)
[![Quarto Publish](https://github.com/iBridges-for-iRods/iBridges-GUI/actions/workflows/publish.yml/badge.svg)](https://github.com/iBridges-for-iRods/iBridges-GUI/actions/workflows/publish.yml)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.12583957.svg)](https://doi.org/10.5281/zenodo.12583957)


Expand Down Expand Up @@ -86,7 +87,7 @@ The git repository contains a generic *iRODS* graphical user interface. The iRO

## Contributing
### Code
Instructions on how to extend the GUI or contribute to the code base can be found in the [documentation](https://chstaiger.github.io/iBridges-Gui/).
Instructions on how to extend the GUI or contribute to the code base can be found in the [documentation](https://ibridges-for-irods.github.io/iBridges-GUI/).

## License
This project is licensed under the GPL-v3 license.
Expand Down
114 changes: 62 additions & 52 deletions ibridgesgui/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import logging
import sys
from typing import Union

import irods.exception
import PyQt6.QtCore
import PyQt6.QtGui
import PyQt6.QtWidgets
import PyQt6.uic
from ibridges import IrodsPath
from ibridges.meta import MetaData
from ibridges.permissions import Permissions
from ibridges.util import obj_replicas

Expand All @@ -26,7 +26,7 @@
class Browser(PyQt6.QtWidgets.QWidget, Ui_tabBrowser):
"""Browser view for iRODS session."""

def __init__(self, session, app_name):
def __init__(self, session, app_name: str):
"""Initialize an iRODS browser view."""
super().__init__()
if getattr(sys, "frozen", False):
Expand Down Expand Up @@ -81,15 +81,15 @@ def init_browser(self):
self.meta_table.clicked.connect(self.edit_metadata)
self.add_meta_button.clicked.connect(self.add_icat_meta)
self.add_meta_button.setToolTip("Add new metadata item.")
self.update_meta_button.clicked.connect(self.set_icat_meta)
self.update_meta_button.setToolTip("Set all entries with the same key to the new values.")
self.update_meta_button.clicked.connect(self.update_icat_meta)
self.update_meta_button.setToolTip("Update the metadata item.")
self.delete_meta_button.clicked.connect(self.delete_icat_meta)
self.delete_meta_button.setToolTip("Delete the metadata item.")
# Manilpulate ACLs
self.acl_table.clicked.connect(self.edit_permission)
self.add_acl_button.clicked.connect(self.update_permission)

def update_input_path(self, irods_path):
def update_input_path(self, irods_path: Union[str, IrodsPath]):
"""Set the input path to a new path and loads the table."""
self.input_path.setText(str(irods_path))
# reset the params to load info tabs
Expand Down Expand Up @@ -253,10 +253,10 @@ def fill_info_tab_content(self):
self.logger.exception("Error loading %s of %s .", tab_name, irods_path)
self.error_label.setText(f"Error loading {tab_name} of {irods_path}: {repr(err)}")

def set_icat_meta(self):
def update_icat_meta(self):
"""Button metadata set."""
try:
self._metadata_edits("set")
self._metadata_edits("update")
except Exception as error:
self.error_label.setText(repr(error))

Expand All @@ -275,7 +275,7 @@ def delete_icat_meta(self):
self.error_label.setText(repr(error))

# @PyQt6.QtCore.pyqtSlot(PyQt6.QtCore.QModelIndex)
def edit_metadata(self, index):
def edit_metadata(self, index: PyQt6.QtCore.QModelIndex):
"""Load selected metadata info edit fields."""
self.error_label.clear()
self.meta_key_field.clear()
Expand All @@ -293,7 +293,7 @@ def edit_metadata(self, index):
self.meta_units_field.setText(units)

# @PyQt6.QtCore.pyqtSlot(PyQt6.QtCore.QModelIndex)
def edit_permission(self, index):
def edit_permission(self, index: PyQt6.QtCore.QModelIndex):
"""Load selected acl into editing fields."""
self.error_label.clear()
self.acl_user_field.clear()
Expand All @@ -317,10 +317,10 @@ def update_permission(self):
acc_name = self.acl_box.currentText()

perm_lables_to_acl = {
"Newly added items to collection will inherit permissions": "inherit",
"Remove inhertiance.": "noinherit",
"delete": "null"
}
"Newly added items to collection will inherit permissions": "inherit",
"Remove inhertiance.": "noinherit",
"delete": "null",
}

if perm_lables_to_acl.get(acc_name, acc_name) in ("inherit", "noinherit"):
if irods_path.dataobject_exists():
Expand All @@ -335,19 +335,29 @@ def update_permission(self):
recursive = self.recursive_box.currentText() == "True"
try:
perm = Permissions(self.session, get_irods_item(irods_path))
perm.set(perm=perm_lables_to_acl.get(acc_name, acc_name),
user=user_name, zone=user_zone, recursive=recursive)
perm.set(
perm=perm_lables_to_acl.get(acc_name, acc_name),
user=user_name,
zone=user_zone,
recursive=recursive,
)
if perm_lables_to_acl.get(acc_name, acc_name) == "null":
self.logger.info(
"Delete access (%s, %s, %s, %s) for %s",
perm_lables_to_acl.get(acc_name, acc_name),
user_name, user_zone, str(recursive), str(irods_path)
user_name,
user_zone,
str(recursive),
str(irods_path),
)
else:
self.logger.info(
"Add/change access of %s to (%s, %s, %s, %s)",
str(irods_path), perm_lables_to_acl.get(acc_name, acc_name),
user_name, user_zone, str(recursive)
str(irods_path),
perm_lables_to_acl.get(acc_name, acc_name),
user_name,
user_zone,
str(recursive),
)
self._fill_acls_tab(irods_path)
except (irods.exception.CAT_INVALID_USER, irods.exception.SYS_NOT_ALLOWED):
Expand All @@ -367,7 +377,7 @@ def _clear_info_tabs(self):
self.preview_browser.clear()
self.no_meta_label.clear()

def _get_item_path(self, row):
def _get_item_path(self, row: int):
item_name = self.browser_table.item(row, 1).text()
return IrodsPath(self.session, "/", *self.input_path.text().split("/"), item_name)

Expand All @@ -386,7 +396,7 @@ def _update_last_selected_row(self):
# fill currently selected tab with info
self.fill_info_tab_content()

def _fill_replicas_tab(self, irods_path):
def _fill_replicas_tab(self, irods_path: Union[IrodsPath, str]):
"""Populate the table in the Replicas tab.
Parameters
Expand All @@ -402,7 +412,7 @@ def _fill_replicas_tab(self, irods_path):
self.replica_table.setRowCount(len(obj.replicas))
self.replica_table.resizeColumnsToContents()

def _fill_acls_tab(self, irods_path):
def _fill_acls_tab(self, irods_path: Union[IrodsPath, str]):
"""Populate the table in the ACLs tab.
Parameters
Expand All @@ -420,7 +430,9 @@ def _fill_acls_tab(self, irods_path):
obj = None
obj_acl_box_items = ["read", "write", "own", "delete"]
coll_acl_box_items = obj_acl_box_items + [
"Newly added items to collection will inherit permissions", "Remove inheritance."]
"Newly added items to collection will inherit permissions",
"Remove inheritance.",
]

if irods_path.collection_exists():
obj = irods_path.collection
Expand All @@ -439,7 +451,7 @@ def _fill_acls_tab(self, irods_path):
self.acl_table.resizeColumnsToContents()
self.owner_label.setText(f"{obj.owner_name}")

def _fill_metadata_tab(self, irods_path):
def _fill_metadata_tab(self, irods_path: Union[IrodsPath, str]):
"""Populate the table in the metadata tab.
Parameters
Expand All @@ -452,19 +464,13 @@ def _fill_metadata_tab(self, irods_path):
self.meta_value_field.clear()
self.meta_units_field.clear()
self.no_meta_label.clear()
item = None
if irods_path.collection_exists():
item = irods_path.collection
elif irods_path.dataobject_exists():
item = irods_path.dataobject
if item is not None:
meta = MetaData(item)
populate_table(self.meta_table, len(list(meta)), meta)
if len(list(meta)) == 0:
if irods_path.exists():
populate_table(self.meta_table, len(list(irods_path.meta)), irods_path.meta)
if len(irods_path.meta) == 0:
self.no_meta_label.setText(f"Metadata for {str(irods_path)} is empty.")
self.meta_table.resizeColumnsToContents()

def _fill_preview_tab(self, irods_path):
def _fill_preview_tab(self, irods_path: Union[IrodsPath, str]):
"""Populate the table in the metadata tab.
Parameters
Expand Down Expand Up @@ -501,7 +507,7 @@ def _fill_preview_tab(self, irods_path):
populate_textfield(self.preview_browser, content)
self.preview_browser.verticalScrollBar().setValue(0)

def _metadata_edits(self, operation):
def _metadata_edits(self, operation: str):
self.error_label.clear()
if self._nothing_selected_error():
return
Expand All @@ -510,23 +516,27 @@ def _metadata_edits(self, operation):
new_key = self.meta_key_field.text()
new_val = self.meta_value_field.text()
new_units = self.meta_units_field.text()
if new_key != "" and new_val != "":
irods_path = self._get_item_path(self.browser_table.currentRow())
if operation == "add":
irods_path.meta.add(new_key, new_val, new_units)
self.logger.info(
"Add metadata (%s, %s, %s) to %s", new_key, new_val, new_units, irods_path
)
elif operation == "set":
irods_path.meta.set(new_key, new_val, new_units)
self.logger.info(
"Set all metadata with key %s to (%s, %s, %s) for %s",
new_key, new_key, new_val, new_units, irods_path
irods_path = self._get_item_path(self.browser_table.currentRow())
if operation == "add":
irods_path.meta.add(new_key, new_val, new_units)
self.logger.info(
"Add metadata (%s, %s, %s) to %s", new_key, new_val, new_units, irods_path
)
elif operation == "update":
row = self.meta_table.currentRow()
old_key = self.meta_table.item(row, 0).text()
old_val = self.meta_table.item(row, 1).text()
old_units = self.meta_table.item(row, 2).text()
print(old_key, old_val, old_units)
self.logger.info(
"Update metadata of %s from (%s, %s, %s) to (%s, %s, %s)",
irods_path, old_key, old_val, old_units,
new_key, new_val, new_units,
)
elif operation == "delete":
irods_path.meta.delete(new_key, new_val, new_units)
self.logger.info(
"Delete metadata (%s, %s, %s) from %s",
new_key, new_val, new_units, irods_path
irods_path.meta[old_key, old_val, old_units] = [new_key, new_val, new_units]
elif operation == "delete":
irods_path.meta.delete(new_key, new_val, new_units)
self.logger.info(
"Delete metadata (%s, %s, %s) from %s", new_key, new_val, new_units, irods_path
)
self._fill_metadata_tab(irods_path)
self._fill_metadata_tab(irods_path)
6 changes: 3 additions & 3 deletions ibridgesgui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@

def ensure_log_config_location():
"""Ensure the location for logs and config files."""
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
CONFIG_DIR.mkdir(parents=True, mode=0o700, exist_ok=True)


def ensure_irods_location():
"""Ensure that .irods exists in user's home."""
irods_loc = Path("~/.irods").expanduser()
irods_loc.mkdir(exist_ok=True)
irods_loc.mkdir(mode=0o700, exist_ok=True)

# logging functions
def init_logger(app_name: str, log_level: str) -> logging.Logger:
Expand Down Expand Up @@ -281,4 +281,4 @@ def _read_json(file_path: Path) -> dict:

def _write_json(file_path: Path, content: dict):
with open(file_path, "w", encoding="utf-8") as handle:
json.dump(content, handle)
json.dump(content, handle, indent=4)
Binary file added ibridgesgui/icons/christmas-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 8 additions & 3 deletions ibridgesgui/login.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Pop up Widget for Login."""

import logging
import os
import sys
from pathlib import Path

Expand All @@ -23,6 +24,10 @@
from ibridgesgui.ui_files.irodsLogin import Ui_irodsLogin


def strictwrite(path, flags, mode=0o600):
"""Create opener for the standard open command to modify the umask."""
return os.open(path, flags, mode)

class Login(QDialog, Ui_irodsLogin):
"""Definition and initialization of the iRODS login window."""

Expand Down Expand Up @@ -51,10 +56,10 @@ def _load_gui(self):
self.envbox.currentTextChanged.connect(self._init_password)

def _init_envbox(self):
env_jsons = [path.name for path in self.irods_config_dir.glob("irods_environment*json")]
env_jsons = [path.name for path in self.irods_config_dir.glob("*.json")]
if len(env_jsons) == 0:
self.error_label.setText(
f"ERROR: no irods_environment*json files found in {self.irods_config_dir}"
f"ERROR: no .json files found in {self.irods_config_dir}"
)

self.envbox.clear()
Expand Down Expand Up @@ -105,7 +110,7 @@ def login_function(self):
try:
if self.cached_pw is True and self.password_field.text() == "***********":
self.logger.debug("Login with %s and cached password.", env_file)
with open(IRODSA, "w", encoding="utf-8") as f:
with open(IRODSA, "w", encoding="utf-8", opener=strictwrite) as f:
f.write(self.prev_settings[str(env_file)])

session = Session(irods_env=env_file)
Expand Down
Loading

0 comments on commit 1d33acd

Please sign in to comment.