Skip to content

Commit

Permalink
Merge branch 'main' into its_the_final_logger
Browse files Browse the repository at this point in the history
  • Loading branch information
mrvisscher authored Aug 9, 2024
2 parents 91afaca + 8e1d44c commit 1f6a3f8
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 29 deletions.
1 change: 1 addition & 0 deletions activity_browser/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

from .method.method_duplicate import MethodDuplicate
from .method.method_delete import MethodDelete
from .method.method_open import MethodOpen

from .method.cf_uncertainty_modify import CFUncertaintyModify
from .method.cf_amount_modify import CFAmountModify
Expand Down
33 changes: 18 additions & 15 deletions activity_browser/actions/activity/activity_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,30 @@ def run(activity_keys: List[tuple]):
# retrieve activity objects from the controller using the provided keys
activities = [bd.get_activity(key) for key in activity_keys]

warning_text = f"Are you certain you want to delete {len(activities)} activity/activities?"

# check for downstream processes
if any(len(act.upstream()) > 0 for act in activities):
# warning text
text = (
"One or more activities have downstream processes. Deleting these activities will remove the "
"exchange from the downstream processes, this can't be undone.\n\nAre you sure you want to "
"continue?"
warning_text += (
"\n\nOne or more activities have downstream processes. Deleting these activities will remove the "
"exchange from the downstream processes as well."
)

# alert the user
choice = QtWidgets.QMessageBox.warning(
application.main_window,
"Activity/Activities has/have downstream processes",
text,
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No,
)
# alert the user
choice = QtWidgets.QMessageBox.warning(
application.main_window,
"Deleting activity/activities",
warning_text,
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No,
)

# return if the user cancels
if choice == QtWidgets.QMessageBox.No:
return


# return if the user cancels
if choice == QtWidgets.QMessageBox.No:
return

# use the activity controller to delete multiple activities
for act in activities:
Expand Down
2 changes: 1 addition & 1 deletion activity_browser/actions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_QButton(cls, *args, **kwargs):
cls.icon,
cls.text
)
button.clicked.connect(lambda x: cls.run(*args, **kwargs))
button.clicked.connect(lambda x: cls.triggered(*args, **kwargs))
return button


Expand Down
26 changes: 26 additions & 0 deletions activity_browser/actions/method/method_open.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import List

from PySide2 import QtWidgets, QtCore

from activity_browser import signals
from activity_browser.actions.base import ABAction, exception_dialogs
from activity_browser.ui.icons import qicons


class MethodOpen(ABAction):
"""
ABAction to open one or more supplied methods in a method tab by employing signals.
TODO: move away from using signals like this. Probably add a method to the MainWindow to add a panel instead.
"""

icon = qicons.right
text = "Open Impact Category"

@staticmethod
@exception_dialogs
def run(method_names: List[tuple]):
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
for method_name in method_names:
signals.method_selected.emit(method_name)
QtWidgets.QApplication.restoreOverrideCursor()
18 changes: 9 additions & 9 deletions activity_browser/bwutils/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,26 +261,26 @@ def unpack_classifications(self, df: pd.DataFrame, systems: list) -> pd.DataFram

def _unpacker(self, classifications: list, system: str) -> list:
"""Iterate over all 'c' lists in 'classifications'
and add those matching 'system' to list 'x', when no matches, add empty string.
and add those matching 'system' to list 'system_classifications', when no matches, add empty string.
If 'c' is not a list, add empty string.
Always returns a list 'x' where len(x) == len(classifications).
Always returns a list 'system_classifications' where len(system_classifications) == len(classifications).
Testing showed that converting to list and doing the checks on a list is ~5x faster than keeping
data in DF and using a df.apply() function, we do this now (difference was ~0.4s vs ~2s).
"""
x = []
system_classifications = []
for c in classifications:
cls = ""
if type(c) != list:
x.append(cls)
result = ""
if not isinstance(c, (list, tuple, set)):
system_classifications.append(result)
continue
for s in c:
if s[0] == system:
cls = s[1]
result = s[1]
break
x.append(cls)
return x
system_classifications.append(result) # result is either "" or the classification
return system_classifications


AB_metadata = MetaDataStore()
1 change: 1 addition & 0 deletions activity_browser/layouts/tabs/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ def __init__(self, key: tuple, read_only=True, parent=None):
self.group_splitter.addWidget(group)
if state := ab_settings.settings.get("activity_table_layout", None):
self.group_splitter.restoreState(bytearray.fromhex(state))
self.group_splitter.setChildrenCollapsible(False)

# Full layout
layout = QtWidgets.QVBoxLayout()
Expand Down
19 changes: 19 additions & 0 deletions activity_browser/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,25 @@ def __init__(self, filename: str):

super().__init__(ab_dir.user_data_dir, filename)

if not self.healthy():
log.warn("Settings health check failed, resetting")
self.restore_default_settings()

def healthy(self) -> bool:
"""
Checks the settings file to see if it is healthy. Returns True if all checks pass, otherwise returns False.
"""
healthy = True

# check for write access to the current bw dir
healthy = healthy and os.access(self.settings.get("current_bw_dir"), os.W_OK)

# check for write access to the custom bw dirs
access = [os.access(path, os.W_OK) for path in self.settings.get("custom_bw_dirs")]
healthy = healthy and False not in access

return healthy

@staticmethod
def update_old_settings(directory: str, filename: str) -> None:
"""Recycling code to enable backward compatibility: This function is only required for compatibility
Expand Down
12 changes: 10 additions & 2 deletions activity_browser/ui/tables/LCA_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from PySide2 import QtWidgets
from PySide2.QtCore import Qt, Slot

from activity_browser import signals
from activity_browser import signals, actions
from activity_browser.mod.bw2data import calculation_setups

from ..icons import qicons
Expand Down Expand Up @@ -178,6 +178,8 @@ def __init__(self, parent=None):
"Hold CTRL and click to select multiple rows to open or delete them."
)

self.open_method_action = actions.MethodOpen.get_QAction(self.selected_methods)

def to_python(self):
return self.model.methods

Expand All @@ -199,11 +201,14 @@ def contextMenuEvent(self, event) -> None:
if self.indexAt(event.pos()).row() == -1:
return
menu = QtWidgets.QMenu()

menu.addAction(self.open_method_action)
menu.addAction(
qicons.delete,
"Remove row",
"Remove rows",
lambda: self.model.delete_rows(self.selectedIndexes()),
)

menu.exec_(event.globalPos())

def dragEnterEvent(self, event):
Expand All @@ -229,6 +234,9 @@ def dropEvent(self, event):
):
self.model.relocateRow(from_index, to_index)

def selected_methods(self):
return [self.model.get_method(p) for p in self.selectedIndexes() if p.column() == 0]


class ScenarioImportTable(ABDataFrameView):
"""Self-contained widget that shows the scenario headers for a given
Expand Down
9 changes: 8 additions & 1 deletion activity_browser/ui/tables/models/lca_setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Iterable
from typing import Iterable, List, Union
from logging import getLogger

import numpy as np
Expand Down Expand Up @@ -195,6 +195,13 @@ def methods(self) -> list:
else self._dataframe.loc[:, "method"].to_list()
)

def get_method(self, proxy: Union[QModelIndex, int]) -> tuple:
"""
Return the method coupled to a model index
"""
idx = self.proxy_to_source(proxy)
return self._dataframe["method"][idx.row()]

def load(self, cs_name: str = None) -> None:
"""
Load a calculation setup defined by cs_name into the methods table.
Expand Down
2 changes: 1 addition & 1 deletion activity_browser/ui/wizards/db_import_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ def run_extract_import(self) -> None:
self.downloader.out_path = self.archive_path
with tempfile.TemporaryDirectory() as tempdir:
temp_dir = Path(tempdir)
self.run_extract(tempdir)
self.run_extract(Path(self.archive_path), temp_dir)
if not import_signals.cancel_sentinel:
# Working with ecoinvent 7z file? look for 'datasets' dir
eco_dir = temp_dir.joinpath("datasets")
Expand Down

0 comments on commit 1f6a3f8

Please sign in to comment.