Skip to content

Commit

Permalink
Small fixes 261 (#472)
Browse files Browse the repository at this point in the history
* Catch unexpected KeyError (deleted activity) issues

* Inherit different table view for scenario table

* Drop SimpleCopy table view class

* Drop SimpleCopy models

* Add shift toggle to include table header on copy

* Ensure correct extension is added to path if not given

* Create separate method for extracting 7z archives

* Add shortcuts for the different local imports

* Simplify run_ecoinvent method
  • Loading branch information
dgdekoning authored Nov 18, 2020
1 parent 6e1b978 commit 47ae264
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 52 deletions.
16 changes: 2 additions & 14 deletions activity_browser/app/ui/tables/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,11 @@ def headerData(self, section, orientation, role=Qt.DisplayRole):
return self._dataframe.index[section]
return None

def to_clipboard(self, rows, columns):
def to_clipboard(self, rows, columns, include_header: bool = False):
""" Copy the given rows and columns of the dataframe to clipboard
"""
self._dataframe.iloc[rows, columns].to_clipboard(index=False)


class SimpleCopyPandasModel(PandasModel):
""" Override the to_clipboard method to exclude copying table headers
"""
def to_clipboard(self, rows, columns):
self._dataframe.iloc[rows, columns].to_clipboard(
index=False, header=False
index=False, header=include_header
)


Expand Down Expand Up @@ -97,11 +90,6 @@ def flags(self, index):
return super().flags(index) | Qt.ItemIsDragEnabled


class SimpleCopyDragPandasModel(SimpleCopyPandasModel):
def flags(self, index):
return super().flags(index) | Qt.ItemIsDragEnabled


class EditableDragPandasModel(EditablePandasModel):
def flags(self, index):
return super().flags(index) | Qt.ItemIsDragEnabled
Expand Down
4 changes: 2 additions & 2 deletions activity_browser/app/ui/tables/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ...bwutils.utils import Parameters
from ...bwutils import presamples as ps_utils
from ...signals import signals
from .views import ABDataFrameSimpleCopy, dataframe_sync
from .views import ABDataFrameView, dataframe_sync


class PresamplesList(QComboBox):
Expand Down Expand Up @@ -51,7 +51,7 @@ def get_package_names() -> List[str]:
return ps_utils.find_all_package_names()


class ScenarioTable(ABDataFrameSimpleCopy):
class ScenarioTable(ABDataFrameView):
""" Constructs an infinitely (horizontally) expandable table that is
used to set specific amount for user-defined parameters.
Expand Down
18 changes: 5 additions & 13 deletions activity_browser/app/ui/tables/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
from ...settings import ab_settings
from .delegates import ViewOnlyDelegate
from .models import (DragPandasModel, EditableDragPandasModel,
EditablePandasModel, PandasModel,
SimpleCopyDragPandasModel, SimpleCopyPandasModel)
EditablePandasModel, PandasModel)


def dataframe_sync(sync):
Expand Down Expand Up @@ -145,23 +144,16 @@ def keyPressEvent(self, e):
NOTE: by default, the table headers (column names) are also copied.
"""
if e.modifiers() and Qt.ControlModifier:
if e.modifiers() & Qt.ControlModifier:
# Should we include headers?
headers = e.modifiers() & Qt.ShiftModifier
if e.key() == Qt.Key_C: # copy
selection = [self.get_source_index(pindex) for pindex in self.selectedIndexes()]
rows = [index.row() for index in selection]
columns = [index.column() for index in selection]
rows = sorted(set(rows), key=rows.index)
columns = sorted(set(columns), key=columns.index)
self.model.to_clipboard(rows, columns)


class ABDataFrameSimpleCopy(ABDataFrameView):
""" A view-only class which copies values without including headers
"""
def _select_model(self) -> QAbstractTableModel:
if hasattr(self, 'drag_model'):
return SimpleCopyDragPandasModel(self.dataframe)
return SimpleCopyPandasModel(self.dataframe)
self.model.to_clipboard(rows, columns, headers)


class ABDataFrameEdit(ABDataFrameView):
Expand Down
11 changes: 9 additions & 2 deletions activity_browser/app/ui/tabs/LCA_results_tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,11 @@ def __init__(self, name: str, presamples=None, parent=None):
def do_calculations(self):
"""Perform the MLCA calculation."""
if self.presamples is None:
self.mlca = MLCA(self.cs_name)
self.contributions = Contributions(self.mlca)
try:
self.mlca = MLCA(self.cs_name)
self.contributions = Contributions(self.mlca)
except KeyError as e:
raise BW2CalcError("LCA Failed", str(e)).with_traceback(e.__traceback__)
elif isinstance(self.presamples, str):
try:
self.mlca = PresamplesMLCA(self.cs_name, self.presamples)
Expand All @@ -136,6 +139,8 @@ def do_calculations(self):
msg = ("Given scenario package refers to non-existent exchanges."
" It is suggested to remove or edit this package.")
raise BW2CalcError(msg, str(e)).with_traceback(e.__traceback__)
except KeyError as e:
raise BW2CalcError("LCA Failed", str(e)).with_traceback(e.__traceback__)
else:
try:
self.mlca = SuperstructureMLCA(self.cs_name, self.presamples)
Expand All @@ -150,6 +155,8 @@ def do_calculations(self):
"Scenario LCA failed.",
"Constructed LCA matrix does not contain any exchanges from the superstructure"
).with_traceback(e.__traceback__)
except KeyError as e:
raise BW2CalcError("LCA Failed", str(e)).with_traceback(e.__traceback__)
self.mlca.calculate()
self.mc = MonteCarloLCA(self.cs_name)

Expand Down
2 changes: 2 additions & 0 deletions activity_browser/app/ui/wizards/db_export_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ def perform_export(self) -> None:
if ext and not ext == EXTENSIONS[export_as]:
ext = EXTENSIONS[export_as]
out_path = path + ext
elif not ext:
out_path = path + EXTENSIONS[export_as]
EXPORTERS[export_as](db_name, out_path)


Expand Down
67 changes: 46 additions & 21 deletions activity_browser/app/ui/wizards/db_import_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,32 +677,32 @@ def run(self):
import_signals.cancel_sentinel = False
if self.use_forwast:
self.run_forwast()
elif self.use_local:
elif self.use_local: # excel or bw2package
self.run_local_import()
elif self.datasets_path: # ecospold2 files
self.run_import(self.datasets_path)
elif self.archive_path: # 7zip file
self.run_extract_import()
else:
self.run_ecoinvent()

def run_ecoinvent(self):
def run_ecoinvent(self) -> None:
"""Run the ecoinvent downloader from start to finish."""
self.downloader.outdir = eidl.eidlstorage.eidl_dir
if self.downloader.check_stored():
import_signals.download_complete.emit()
else:
self.run_download()

with tempfile.TemporaryDirectory() as tempdir:
dataset_dir = self.datasets_path or os.path.join(tempdir, "datasets")
if not os.path.isdir(dataset_dir):
if self.archive_path is None:
self.downloader.outdir = eidl.eidlstorage.eidl_dir
if self.downloader.check_stored():
import_signals.download_complete.emit()
else:
self.run_download()
else:
self.downloader.out_path = self.archive_path
if not import_signals.cancel_sentinel:
self.run_extract(tempdir)
if not import_signals.cancel_sentinel:
self.run_extract(tempdir)
if not import_signals.cancel_sentinel:
dataset_dir = os.path.join(tempdir, "datasets")
self.run_import(dataset_dir)

def run_forwast(self):
"""
adapted from pjamesjoyce/lcopt
"""
def run_forwast(self) -> None:
"""Adapted from pjamesjoyce/lcopt."""
response = requests.get(self.forwast_url)
forwast_zip = zipfile.ZipFile(io.BytesIO(response.content))
import_signals.download_complete.emit()
Expand All @@ -725,15 +725,40 @@ def run_forwast(self):
else:
self.delete_canceled_db()

def run_download(self):
def run_download(self) -> None:
"""Use the connected ecoinvent downloader."""
self.downloader.download()
import_signals.download_complete.emit()

def run_extract(self, temp_dir):
def run_extract(self, temp_dir) -> None:
"""Use the connected ecoinvent downloader to extract the downloaded
7zip file.
"""
self.downloader.extract(target_dir=temp_dir)
import_signals.unarchive_finished.emit()

def run_import(self, import_dir):
def run_extract_import(self) -> None:
"""Combine the extract and import steps when beginning from a selected
7zip archive.
By default, look in the 'datasets' folder because this is how ecoinvent
7zip archives are structured. If this folder is not found, fall back
to using the temporary directory instead.
"""
self.downloader.out_path = self.archive_path
with tempfile.TemporaryDirectory() as tempdir:
self.run_extract(tempdir)
if not import_signals.cancel_sentinel:
# Working with ecoinvent 7z file? look for 'datasets' dir
eco_dir = os.path.join(tempdir, "datasets")
if os.path.exists(eco_dir) and os.path.isdir(eco_dir):
self.run_import(eco_dir)
else:
# Use the temp dir itself instead.
self.run_import(tempdir)

def run_import(self, import_dir) -> None:
"""Use the given dataset path to import the ecospold2 files."""
try:
importer = SingleOutputEcospold2Importer(
import_dir,
Expand Down

0 comments on commit 47ae264

Please sign in to comment.