Skip to content

Commit

Permalink
Merge pull request #41 from polca/ab210_fixes
Browse files Browse the repository at this point in the history
Make ScenarioLink AB=2.10 compatible
  • Loading branch information
romainsacchi authored Aug 26, 2024
2 parents 95ca7f4 + 939f838 commit 48e6b16
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 95 deletions.
2 changes: 1 addition & 1 deletion ab_plugin_scenariolink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Plugin(ab.Plugin):

def __init__(self):
infos = {
'name': "ScenarioLink",
"name": "ScenarioLink",
}
ab.Plugin.__init__(self, infos)

Expand Down
79 changes: 40 additions & 39 deletions ab_plugin_scenariolink/layouts/tabs/right.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import List, Tuple
from unfold.unfold import clear_cache

from activity_browser import log
from activity_browser.layouts.tabs import PluginTab
from activity_browser.ui.style import horizontal_line, header
from activity_browser.ui.widgets.dialog import DatabaseLinkingDialog
Expand All @@ -22,7 +23,7 @@ def __init__(self, plugin, parent=None):
self.fold_chooser = FoldChooserWidget()
self.scenario_chooser = ScenarioChooserWidget()

self.version_label = QtWidgets.QLabel('')
self.version_label = QtWidgets.QLabel("")

self.construct_layout()
self.version_check()
Expand All @@ -37,7 +38,7 @@ def construct_layout(self) -> None:
self.layout.setAlignment(QtCore.Qt.AlignTop)

# Header
self.layout.addWidget(header(self.plugin.infos['name']))
self.layout.addWidget(header(self.plugin.infos["name"]))
self.layout.addWidget(horizontal_line())

# Folds chooser
Expand Down Expand Up @@ -76,8 +77,8 @@ def generate_database(self, include_scenarios, dependencies, as_superstructure,
def version_check(self) -> None:
newer, current, latest = UpdateManager.get_versions()
if newer:
label = 'A newer version of ScenarioLink is available (your version: {}, the newest version: {})'\
.format(current, latest)
label = (f"A newer version of ScenarioLink is available (your version: {current}, "
f"the newest version: {latest})")
self.version_label.setText(label)


Expand All @@ -89,17 +90,17 @@ def __init__(self):
self.custom_package_path = None

# label
self.label = QtWidgets.QLabel('Select the datapackage you want to use')
self.label = QtWidgets.QLabel("Select the datapackage you want to use")
self.layout.addWidget(self.label)

# Radio buttons to choose where to get Fold from
self.radio_default = QtWidgets.QRadioButton('Online datapackages')
self.radio_default = QtWidgets.QRadioButton("Online datapackages")
self.radio_default.setChecked(True)
self.radio_custom = QtWidgets.QRadioButton('Local datapackages')
self.clear_datapackage_cache = QtWidgets.QPushButton('Clear datapackage cache')
self.radio_custom = QtWidgets.QRadioButton("Local datapackages")
self.clear_datapackage_cache = QtWidgets.QPushButton("Clear datapackage cache")
self.clear_datapackage_cache.setToolTip(
'ScenarioLink caches the downloaded datapackages, though sometimes\n'
'these may be updated and you need to clear the cache.'
"ScenarioLink caches the downloaded datapackages, though sometimes\n"
"these may be updated and you need to clear the cache."
)
self.radio_layout = QtWidgets.QHBoxLayout()
self.radio_layout.addWidget(self.radio_default)
Expand All @@ -111,21 +112,21 @@ def __init__(self):
self.layout.addWidget(self.radio_widget)

# Folds table
self.table_label = QtWidgets.QLabel('Doubleclick to open a datapackage (if not present locally, it will be downloaded - this may take a while).')
self.table_label = QtWidgets.QLabel("Doubleclick to open a datapackage (if not present locally, it will be downloaded - this may take a while).")
self.layout.addWidget(self.table_label)

self.folds_table = FoldsTable(self)
self.use_table = True # bool to see if we need to read this table or instead read the local import
if self.folds_table.model.df_columns.get('link', False):
self.folds_table.setToolTip('Doubleclick to open a datapackage\n'
'Right click to open a dashboard with more information')
if self.folds_table.model.df_columns.get("link", False):
self.folds_table.setToolTip("Doubleclick to open a datapackage\n"
"Right click to open a dashboard with more information")
else:
self.folds_table.setToolTip('Doubleclick to open a datapackage')
self.folds_table.setToolTip("Doubleclick to open a datapackage")
self.layout.addWidget(self.folds_table)

# Fold custom importer
self.custom_layout = QtWidgets.QHBoxLayout()
self.custom = QtWidgets.QPushButton('Browse computer')
self.custom = QtWidgets.QPushButton("Browse computer")
self.custom.setVisible(False)
self.custom_layout.addWidget(self.custom)
self.custom_layout.addStretch()
Expand Down Expand Up @@ -154,14 +155,14 @@ def get_datapackage_custom_path(self) -> None:
""""Start a dialog to retrieve a datapackage from disk."""
path, _ = QtWidgets.QFileDialog.getOpenFileName(
caption="Select datapackage zip file",
filter='*.zip'
filter="*.zip"
)
print('file selected from path:', path)
log.info(f"file selected from path: {path}")
self.custom_package_path = path
signals.get_datapackage_from_disk.emit(path)

def do_clear_cache(self) -> None:
print('Clearing the datapackage cache')
log.info("Clearing the datapackage cache")
clear_sl_datapackage_cache()
self.folds_table.model.sync()

Expand All @@ -174,22 +175,22 @@ def __init__(self):
self.sdf_path = None

# Label
self.label = QtWidgets.QLabel('Choose the scenarios you want to install')
self.label = QtWidgets.QLabel("Choose the scenarios you want to install")
self.layout.addWidget(self.label)

# Datapackage table
self.data_package_table = DataPackageTable(self)
self.layout.addWidget(self.data_package_table)

# SDF checker
self.sdf_check = QtWidgets.QCheckBox('Produce Superstructure database')
self.sdf_check = QtWidgets.QCheckBox("Produce Superstructure database")
self.sdf_check.setChecked(False)
self.sdf_check.setEnabled(False)
self.sdf_name_field = QtWidgets.QLineEdit()
self.sdf_name_field.setPlaceholderText('Superstructure database name (optional)')
self.sdf_name_field.setPlaceholderText("Superstructure database name (optional)")
self.sdf_name_field.setEnabled(False)
self.sdf_file_loc = QtWidgets.QPushButton('SDF location')
self.sdf_file_loc.setToolTip('Choose a folder to export the SDF scenario file to')
self.sdf_file_loc = QtWidgets.QPushButton("SDF location")
self.sdf_file_loc.setToolTip("Choose a folder to export the SDF scenario file to")
self.sdf_file_loc.setEnabled(False)

self.sdf_layout = QtWidgets.QHBoxLayout()
Expand All @@ -198,20 +199,20 @@ def __init__(self):
self.sdf_layout.addWidget(self.sdf_file_loc)
self.sdf_layout.addStretch()
self.sdf_widget = QtWidgets.QWidget()
self.sdf_widget.setToolTip('Instead of writing multiple databases per scenario,\n'
'write one database and an SDF scenario difference file')
self.sdf_widget.setToolTip("Instead of writing multiple databases per scenario,\n""
"write one database and an SDF scenario difference file")
self.sdf_widget.setLayout(self.sdf_layout)
self.layout.addWidget(self.sdf_widget)

# Import button
self.import_b = QtWidgets.QPushButton('Import')
self.import_b = QtWidgets.QPushButton("Import")
self.import_b.setEnabled(False)
self.import_layout = QtWidgets.QHBoxLayout()
self.import_layout.addWidget(self.import_b)
self.import_layout.addStretch()
self.clear_unfold_cache = QtWidgets.QPushButton('Clear unfold cache')
self.clear_unfold_cache.setToolTip('Unfold caches some data to work faster, though sometimes this can store old data\n'
'that should be renewed, clearing the cache allows new data to be cached.')
self.clear_unfold_cache = QtWidgets.QPushButton("Clear unfold cache")
self.clear_unfold_cache.setToolTip("Unfold caches some data to work faster, though sometimes this can store old data\n"
"that should be renewed, clearing the cache allows new data to be cached.")
self.import_layout.addWidget(self.clear_unfold_cache)
self.import_b_widg = QtWidgets.QWidget()
self.import_b_widg.setLayout(self.import_layout)
Expand All @@ -228,7 +229,7 @@ def __init__(self):
self.sdf_file_loc.clicked.connect(self.choose_sdf_location)

def do_clear_cache(self):
print('Clearing the unfold cache')
log.info("Clearing the unfold cache")
clear_cache()

def import_state(self):
Expand All @@ -238,24 +239,24 @@ def import_state(self):

# match the dependencies (databases) of the scenarios to the correct databases in AB
dependencies = []
for dependency in self.data_package_table.model.data_package.descriptor['dependencies']:
dependencies.append(dependency['name'])
for dependency in self.data_package_table.model.data_package.descriptor["dependencies"]:
dependencies.append(dependency["name"])
dependencies = self.relink_database(dependencies)
if not dependencies:
return

# read/set the correct data for SDF
as_sdf = self.sdf_check.isChecked()
sdf_db = self.sdf_name_field.text()
if sdf_db == '' and not as_sdf:
if sdf_db == "" and not as_sdf:
sdf_db = None
elif sdf_db == '' and as_sdf:
elif sdf_db == "" and as_sdf:
# no name was chosen for the superstructure, generate a descriptive name
db = [db for db in dependencies.values() if db != 'biosphere3'][0] # get db name
db = [db for db in dependencies.values() if db != "biosphere3"][0] # get db name
scn = self.data_package_table.model.scenario_name
sdf_db = ' - '.join([db, scn])
sdf_db = " - ".join([db, scn])
sdf_loc = self.sdf_file_loc
if sdf_loc == '':
if sdf_loc == "":
sdf_loc = None

# start database generation
Expand Down Expand Up @@ -310,7 +311,7 @@ def __init__(self, parent=None):

@classmethod
def relink_scenario_link(cls, options: List[Tuple[str, List[str]]],
parent=None) -> 'RelinkDialog':
parent=None) -> "RelinkDialog":
label = "Choose the ScenarioLink databases.\nBy clicking 'OK', you start the import."
return cls.construct_dialog(label, options, parent)

21 changes: 12 additions & 9 deletions ab_plugin_scenariolink/tables/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
This module contains the models for the tables used in the ScenarioLink plugin.
"""

from logging import getLogger
from urllib.error import HTTPError, URLError
import pandas as pd

from activity_browser.ui.tables.models import PandasModel
from ..utils import download_files_from_zenodo, package_from_path, record_cached
from ..signals import signals

log = getLogger(__name__)


class FoldsModel(PandasModel):
"""
Expand Down Expand Up @@ -43,27 +46,27 @@ def sync(self):
dataframe = pd.read_csv(url + "?nocache", header=0, sep=";", dtype=str)

cached = []
rec_col = dataframe.columns.tolist().index('Zenodo record ID')
rec_col = dataframe.columns.tolist().index("Zenodo record ID")
for idx, row in dataframe.iterrows():
record_id = row.values.tolist()[rec_col]
cached.append(record_cached(record_id))
dataframe['downloaded'] = cached
dataframe["downloaded"] = cached

self._dataframe = dataframe
except (HTTPError, URLError) as exception:
print('++Failed to import data:', exception)
log.error(f"Failed to import data: {exception}")

self.df_columns = {n: i for i, n in enumerate(dataframe.columns.tolist())}
self.updated.emit()

def get_record(self, idx):
"""Retrieve a record from a selected row in the DataFrame."""
record = self._dataframe.iat[idx.row(), self.df_columns['Zenodo record ID']]
record = self._dataframe.iat[idx.row(), self.df_columns["Zenodo record ID"]]
self.selected_record = record
return record

def get_link(self, row: int) -> str:
return self._dataframe.iloc[row, self.df_columns['link']]
return self._dataframe.iloc[row, self.df_columns["link"]]

def record_ready(self, ready: bool) -> None:
if ready:
Expand Down Expand Up @@ -100,8 +103,8 @@ def sync(self) -> None:
return

datapackage = self.data_package
dataframe = self.build_df_from_descriptor(datapackage.descriptor['scenarios'])
dataframe = dataframe.reindex(columns=['include', 'name', 'description'])
dataframe = self.build_df_from_descriptor(datapackage.descriptor["scenarios"])
dataframe = dataframe.reindex(columns=["include", "name", "description"])

self._dataframe = dataframe
self.updated.emit()
Expand All @@ -122,7 +125,7 @@ def build_df_from_descriptor(self, descr: list) -> pd.DataFrame:
if len(self.include) <= 1:
signals.no_or_1_scenario_selected.emit(True)

data = {'include': self.include}
data = {"include": self.include}

for dict_ in descr:
for key, value in dict_.items():
Expand All @@ -131,7 +134,7 @@ def build_df_from_descriptor(self, descr: list) -> pd.DataFrame:
else:
data[key] = [value]

name = data.get('name', [False])[0]
name = data.get("name", [False])[0]
if name:
# this only works because we know a scenario name ends in ' - YYYY', if that changes, this fails
self.scenario_name = name[:-7]
Expand Down
12 changes: 5 additions & 7 deletions ab_plugin_scenariolink/tables/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class FoldsTable(ABDataFrameView):
def __init__(self, parent=None):
"""Initialize the FoldsTable."""
super().__init__(parent)
self.vis_columns = ['generator', 'generator version', 'creation date', 'scope',
'model', 'scenario', 'source database', 'downloaded']
self.vis_columns = ["generator", "generator version", "creation date", "scope",
"model", "scenario", "source database", "downloaded"]

# Hide the vertical header and set the selection mode
self.verticalHeader().setVisible(False)
Expand All @@ -43,7 +43,7 @@ def __init__(self, parent=None):
self.model.sync()

# Specify the column index for the 'cached column' checkbox
self.cached_col = self.model.df_columns['downloaded']
self.cached_col = self.model.df_columns["downloaded"]
self.setItemDelegateForColumn(self.cached_col, CheckboxDelegate(self))

# only show the columns in self.vis_columns and present in the dataframe
Expand All @@ -55,21 +55,20 @@ def _connect_signals(self):
"""Connect signals to slots."""
self.doubleClicked.connect(self.row_selected)
self.model.updated.connect(self.update_proxy_model)
self.model.updated.connect(self.custom_view_sizing)
self.model.updated.connect(self.update_col_width)

def contextMenuEvent(self, event: QContextMenuEvent) -> None:
""" Have the parameter test to see if it can be deleted safely.
"""
if self.indexAt(event.pos()).row() == -1:
return
if not self.model.df_columns.get('link', False):
if not self.model.df_columns.get("link", False):
# the CSV with scenarios does not have a link
return

action = QtWidgets.QAction("Open dashboard in browser")
action.triggered.connect(self.open_link)
action.setToolTip('Open a dashboard with more information about this scenario in the browser')
action.setToolTip("Open a dashboard with more information about this scenario in the browser")
menu = QtWidgets.QMenu(self)
menu.addAction(action)
menu.exec_(event.globalPos())
Expand Down Expand Up @@ -117,7 +116,6 @@ def __init__(self, parent=None):
def _connect_signals(self):
"""Connect signals to slots."""
self.model.updated.connect(self.update_proxy_model)
self.model.updated.connect(self.custom_view_sizing)
self.model.updated.connect(lambda: signals.record_ready.emit(True))
self.model.updated.connect(self.update_col_width)

Expand Down
Loading

0 comments on commit 48e6b16

Please sign in to comment.