Skip to content

Commit

Permalink
Merge branch 'ConservationInternational:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
ketankartoza authored Nov 14, 2024
2 parents 64a36a5 + ca65a19 commit e7222cf
Show file tree
Hide file tree
Showing 20 changed files with 682 additions and 133 deletions.
16 changes: 13 additions & 3 deletions src/cplus_plugin/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ def fetch_scenario_output(
download_paths = []
final_output = None
for output in output_list["results"]:
urls_to_download.append(output["url"])
if output["is_final_output"]:
download_path = os.path.join(scenario_directory, output["filename"])
final_output_path = download_path
Expand All @@ -170,9 +169,14 @@ def fetch_scenario_output(
download_path = os.path.join(
scenario_directory, output["group"], output["filename"]
)
urls_to_download.append(output["url"])
download_paths.append(download_path)

download_paths_copy = copy.deepcopy(download_paths)

# configure max number of iteration
max_iteration = len(download_paths_copy * 2)
iteration = 0
while len(urls_to_download) > 0:
with concurrent.futures.ThreadPoolExecutor(
max_workers=3 if os.cpu_count() > 3 else 1
Expand All @@ -181,8 +185,14 @@ def fetch_scenario_output(
if self.is_download_cancelled():
return None, None
is_valid, invalid_indexes = self.__validate_output_paths(download_paths)
urls_to_download = [urls_to_download[idx] for idx in invalid_indexes]
download_paths_copy = [download_paths_copy[idx] for idx in invalid_indexes]
if len(invalid_indexes) <= len(urls_to_download):
urls_to_download = [urls_to_download[idx] for idx in invalid_indexes]
download_paths_copy = [
download_paths_copy[idx] for idx in invalid_indexes
]
if iteration == max_iteration:
break
iteration += 1

if not self.__validate_output_paths(download_paths):
return None, None
Expand Down
3 changes: 3 additions & 0 deletions src/cplus_plugin/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
NCS_CARBON_SEGMENT,
NCS_PATHWAY_SEGMENT,
NPV_COLLECTION_PROPERTY,
MASK_PATHS_SEGMENT,
PATH_ATTRIBUTE,
PATHWAYS_ATTRIBUTE,
PIXEL_VALUE_ATTRIBUTE,
Expand Down Expand Up @@ -1293,13 +1294,15 @@ def save_activity(self, activity: typing.Union[Activity, dict]):
priority_layers = activity.priority_layers
layer_styles = activity.layer_styles
style_pixel_value = activity.style_pixel_value
mask_paths = activity.mask_paths

ncs_pathways = []
for ncs in activity.pathways:
ncs_pathways.append(str(ncs.uuid))

activity = layer_component_to_dict(activity)
activity[PRIORITY_LAYERS_SEGMENT] = priority_layers
activity[MASK_PATHS_SEGMENT] = mask_paths
activity[PATHWAYS_ATTRIBUTE] = ncs_pathways
activity[STYLE_ATTRIBUTE] = layer_styles
activity[PIXEL_VALUE_ATTRIBUTE] = style_pixel_value
Expand Down
1 change: 1 addition & 0 deletions src/cplus_plugin/definitions/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
NCS_PATHWAY_SEGMENT = "ncs_pathways"
NCS_CARBON_SEGMENT = "ncs_carbon"
PRIORITY_LAYERS_SEGMENT = "priority_layers"
MASK_PATHS_SEGMENT = "mask_paths"
NPV_PRIORITY_LAYERS_SEGMENT = "npv"
COMPARISON_REPORT_SEGMENT = "comparison_reports"

Expand Down
126 changes: 125 additions & 1 deletion src/cplus_plugin/gui/activity_editor_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
)
from qgis.gui import QgsGui, QgsMessageBar

from qgis.PyQt import QtGui, QtWidgets
from qgis.PyQt import QtCore, QtGui, QtWidgets

from qgis.PyQt.uic import loadUiType

Expand Down Expand Up @@ -74,6 +74,7 @@ def __init__(self, parent=None, activity=None, excluded_names=None):

self._edit_mode = False
self._layer = None
self._mask_layer = None

self._excluded_names = excluded_names
if excluded_names is None:
Expand All @@ -93,6 +94,32 @@ def __init__(self, parent=None, activity=None, excluded_names=None):
# Hide map layer handling
self.layer_gb.setVisible(False)

# Mask layers
add_icon = FileUtils.get_icon("symbologyAdd.svg")
self.btn_add_mask.setIcon(add_icon)
self.btn_add_mask.clicked.connect(self._on_add_mask_layer)

remove_icon = FileUtils.get_icon("symbologyRemove.svg")
self.btn_delete_mask.setIcon(remove_icon)
self.btn_delete_mask.setEnabled(False)
self.btn_delete_mask.clicked.connect(self._on_remove_mask_layer)

edit_icon = FileUtils.get_icon("mActionToggleEditing.svg")
self.btn_edit_mask.setIcon(edit_icon)
self.btn_edit_mask.setEnabled(False)
self.btn_edit_mask.clicked.connect(self._on_edit_mask_layer)

if self._activity is not None:
mask_paths_list = self._activity.mask_paths

for mask_path in mask_paths_list or []:
if mask_path == "":
continue
item = QtWidgets.QListWidgetItem()
item.setData(QtCore.Qt.DisplayRole, mask_path)
self.lst_mask_layers.addItem(item)
self.mask_layers_changed()

@property
def activity(self) -> Activity:
"""Returns a reference to the activity object.
Expand Down Expand Up @@ -186,6 +213,88 @@ def _add_layer_path(self, layer_path: str):
else:
self.cbo_layer.setCurrentIndex(matching_index)

def _on_add_mask_layer(self, activated: bool):
"""Slot raised to add a mask layer."""
data_dir = settings_manager.get_value(Settings.LAST_MASK_DIR, default=None)

if not data_dir:
data_dir = os.path.expanduser("~")

mask_path = self._show_mask_path_selector(data_dir)
if not mask_path:
return

item = QtWidgets.QListWidgetItem()
item.setData(QtCore.Qt.DisplayRole, mask_path)

if self.lst_mask_layers.findItems(mask_path, QtCore.Qt.MatchExactly):
error_tr = tr("The selected mask layer already exists.")
self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
return

self.lst_mask_layers.addItem(item)
settings_manager.set_value(Settings.LAST_MASK_DIR, os.path.dirname(mask_path))

self.mask_layers_changed()

def _on_edit_mask_layer(self, activated: bool):
"""Slot raised to edit a mask layer."""

item = self.lst_mask_layers.currentItem()
if not item:
error_tr = tr("Select a mask layer first.")
self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
return
mask_path = self._show_mask_path_selector(item.data(QtCore.Qt.DisplayRole))
if not mask_path:
return

if self.lst_mask_layers.findItems(mask_path, QtCore.Qt.MatchExactly):
error_tr = tr("The selected mask layer already exists.")
self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
return

item.setData(QtCore.Qt.DisplayRole, mask_path)

def _on_remove_mask_layer(self, activated: bool):
"""Slot raised to remove one or more selected mask layers."""
items = self.lst_mask_layers.selectedItems()
if not items:
error_tr = tr("Select the target mask layer first, before removing it.")
self.message_bar.pushMessage(error_tr, qgis.core.Qgis.MessageLevel.Warning)
return

reply = QtWidgets.QMessageBox.warning(
self,
tr("QGIS CPLUS PLUGIN | Settings"),
tr("Remove the selected mask layer(s)?"),
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No,
)

if reply == QtWidgets.QMessageBox.Yes:
for item in items:
item_row = self.lst_mask_layers.row(item)
self.lst_mask_layers.takeItem(item_row)

self.mask_layers_changed()

def _show_mask_path_selector(self, layer_dir: str) -> str:
"""Show file selector dialog for selecting a mask layer."""
filter_tr = tr("Shapefiles")

layer_path, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
self.tr("Select mask Layer"),
layer_dir,
f"{filter_tr} (*.shp)",
options=QtWidgets.QFileDialog.DontResolveSymlinks,
)
if not layer_path:
return ""

return layer_path

def validate(self) -> bool:
"""Validates if name has been specified.
Expand Down Expand Up @@ -266,6 +375,15 @@ def _create_activity(self):
ACTIVITY_LAYER_STYLE_ATTRIBUTE
] = color_ramp_info

# Mask layers settings
mask_paths = []
for row in range(0, self.lst_mask_layers.count()):
item = self.lst_mask_layers.item(row)
item_path = item.data(QtCore.Qt.DisplayRole)
mask_paths.append(item_path)

self._activity.mask_paths = mask_paths

def _get_selected_map_layer(self) -> typing.Union[QgsRasterLayer, None]:
"""Returns the currently selected map layer or None if there is
no item in the combobox.
Expand Down Expand Up @@ -358,3 +476,9 @@ def _on_select_file(self, activated: bool):

self._add_layer_path(layer_path)
settings_manager.set_value(Settings.LAST_DATA_DIR, os.path.dirname(layer_path))

def mask_layers_changed(self):
contains_items = self.lst_mask_layers.count() > 0

self.btn_edit_mask.setEnabled(contains_items)
self.btn_delete_mask.setEnabled(contains_items)
1 change: 1 addition & 0 deletions src/cplus_plugin/gui/qgis_cplus_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,7 @@ def run_analysis(self):
Creates new QgsTask, progress dialog and report manager
for each new scenario analysis.
"""
self.log_text_box.clear()

extent_list = PILOT_AREA_EXTENT["coordinates"]
default_extent = QgsRectangle(
Expand Down
103 changes: 76 additions & 27 deletions src/cplus_plugin/gui/settings/cplus_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,26 +159,39 @@ def register(self):

if resp:
self.close()
if resp.get("status", 200) == 200:
QtWidgets.QMessageBox.information(
None,
self.tr("Success"),
self.tr(
"User registered. Your password "
f"has been emailed to {self.email.text()}. "
"Enter that password in CPLUS settings "
"to finish setting up the plugin."
),
)
# add a new auth conf that have to be completed with pwd
authConfigId = auth.init_auth_config(
auth.TE_API_AUTH_SETUP, email=self.email.text()
)

if authConfigId:
self.authConfigInitialised.emit(authConfigId)
return authConfigId
else:
QtWidgets.QMessageBox.information(
None,
self.tr("Registration failed"),
self.tr(resp.get("detail", "")),
)
else:
QtWidgets.QMessageBox.information(
None,
self.tr("Success"),
self.tr("Failed"),
self.tr(
"User registered. Your password "
f"has been emailed to {self.email.text()}. "
"Enter that password in CPLUS settings "
"to finish setting up the plugin."
"Failed to register. Please check your internet connection and try again."
),
)

# add a new auth conf that have to be completed with pwd
authConfigId = auth.init_auth_config(
auth.TE_API_AUTH_SETUP, email=self.email.text()
)

if authConfigId:
self.authConfigInitialised.emit(authConfigId)
return authConfigId
else:
return None


Expand Down Expand Up @@ -302,13 +315,29 @@ def update_profile(self):
)

if resp:
if resp.get("status", 200) == 200:
QtWidgets.QMessageBox.information(
None,
self.tr("Saved"),
self.tr("Updated information for {}.").format(self.email.text()),
)
self.close()
self.ok = True
else:
QtWidgets.QMessageBox.information(
None,
self.tr("Failed"),
self.tr(resp.get("detail", "")),
)
self.close()
else:
QtWidgets.QMessageBox.information(
None,
self.tr("Saved"),
self.tr("Updated information for {}.").format(self.email.text()),
self.tr("Failed"),
self.tr(
"Failed to update user information. Please check your internet connection and try again."
),
)
self.close()
self.ok = True


class DlgSettingsEditForgotPassword(
Expand Down Expand Up @@ -360,17 +389,33 @@ def reset_password(self):
resp = self.trends_earth_api_client.recover_pwd(self.email.text())

if resp:
self.close()
if resp.get("status", 200) == 200:
self.close()
QtWidgets.QMessageBox.information(
None,
self.tr("Success"),
self.tr(
f"The password has been reset for {self.email.text()}. "
"Check your email for the new password, and then "
"return to Trends.Earth to enter it."
),
)
self.ok = True
else:
self.close()
QtWidgets.QMessageBox.information(
None,
self.tr("Failed"),
self.tr(resp.get("detail", "")),
)
else:
QtWidgets.QMessageBox.information(
None,
self.tr("Success"),
self.tr("Failed"),
self.tr(
f"The password has been reset for {self.email.text()}. "
"Check your email for the new password, and then "
"return to Trends.Earth to enter it."
"Failed to reset password. Please check your internet connection and try again."
),
)
self.ok = True


class CplusSettings(Ui_DlgSettings, QgsOptionsPageWidget):
Expand Down Expand Up @@ -825,8 +870,12 @@ def delete(self):
# remove currently used config (as set in QSettings) and
# trigger GUI
auth.remove_current_auth_config(auth.TE_API_AUTH_SETUP)
# self.reloadAuthConfigurations()
# self.authConfigUpdated.emit()
else:
QtWidgets.QMessageBox.information(
None,
self.tr("Failed"),
self.tr("Failed to delete user."),
)

def reloadAuthConfigurations(self):
authConfigId = settings.value(
Expand Down
Loading

0 comments on commit e7222cf

Please sign in to comment.