Skip to content

Commit b47d38f

Browse files
authored
Merge pull request #153 from opengisch/param-ux
Improve parameters handling
2 parents 53e99bb + 31432d2 commit b47d38f

File tree

8 files changed

+324
-92
lines changed

8 files changed

+324
-92
lines changed

oqtopus/core/package_prepare_task.py

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ def run(self):
9090
self.__destination_directory = self.__prepare_destination_directory()
9191
logger.info(f"Destination directory: {self.__destination_directory}")
9292

93+
# For branches/PRs, always fetch the latest commit SHA before
94+
# checking the cache so we detect new commits
95+
if self.module_package.type in (
96+
ModulePackage.Type.BRANCH,
97+
ModulePackage.Type.PULL_REQUEST,
98+
):
99+
self.module_package.fetch_commit_sha()
100+
93101
# Reset progress tracking
94102
self.__download_total_expected = 0
95103
self.__download_total_received = 0
@@ -128,9 +136,14 @@ def __prepare_module_assets(self, module_package):
128136
self.__prefetch_download_sizes(module_package)
129137

130138
# Download the source or use from zip
131-
zip_file = self.from_zip_file or self.__download_module_asset(
132-
module_package.download_url, "source.zip", module_package
133-
)
139+
if self.from_zip_file:
140+
zip_file = self.from_zip_file
141+
else:
142+
zip_file, was_downloaded = self.__download_module_asset(
143+
module_package.download_url, "source.zip", module_package
144+
)
145+
if was_downloaded:
146+
self.__remove_extracted_dir("src")
134147

135148
module_package.source_package_zip = zip_file
136149
package_dir = self.__extract_zip_file(zip_file, "src")
@@ -139,22 +152,26 @@ def __prepare_module_assets(self, module_package):
139152
# Download the release assets
140153
self.__checkForCanceled()
141154
if module_package.asset_project is not None:
142-
zip_file = self.__download_module_asset(
155+
zip_file, was_downloaded = self.__download_module_asset(
143156
module_package.asset_project.download_url,
144157
module_package.asset_project.type.value + ".zip",
145158
module_package,
146159
)
160+
if was_downloaded:
161+
self.__remove_extracted_dir("project")
147162
package_dir = self.__extract_zip_file(zip_file, "project")
148163
module_package.asset_project.package_zip = zip_file
149164
module_package.asset_project.package_dir = package_dir
150165

151166
self.__checkForCanceled()
152167
if module_package.asset_plugin is not None:
153-
zip_file = self.__download_module_asset(
168+
zip_file, was_downloaded = self.__download_module_asset(
154169
module_package.asset_plugin.download_url,
155170
module_package.asset_plugin.type.value + ".zip",
156171
module_package,
157172
)
173+
if was_downloaded:
174+
self.__remove_extracted_dir("plugin")
158175
package_dir = self.__extract_zip_file(zip_file, "plugin")
159176
module_package.asset_plugin.package_zip = zip_file
160177
module_package.asset_plugin.package_dir = package_dir
@@ -256,6 +273,26 @@ def __get_cache_filename(self, base_filename: str, module_package):
256273
return f"{name}-{int(time.time())}{ext}"
257274
return base_filename
258275

276+
def __remove_extracted_dir(self, subdir):
277+
"""Remove an extracted directory to force re-extraction."""
278+
package_dir = os.path.join(self.__destination_directory, subdir)
279+
if os.path.exists(package_dir):
280+
shutil.rmtree(package_dir)
281+
logger.info(f"Removed stale extracted directory: {subdir}")
282+
283+
def __cleanup_old_cached_files(self, base_filename, current_cache_filename):
284+
"""Remove old SHA-versioned cache files for the same asset."""
285+
name, ext = os.path.splitext(base_filename)
286+
prefix = f"{name}-"
287+
for f in os.listdir(self.__destination_directory):
288+
if f.startswith(prefix) and f.endswith(ext) and f != current_cache_filename:
289+
old_file = os.path.join(self.__destination_directory, f)
290+
try:
291+
os.remove(old_file)
292+
logger.info(f"Removed old cached file: {f}")
293+
except OSError as e:
294+
logger.warning(f"Failed to remove old cached file {f}: {e}")
295+
259296
def __download_module_asset(self, url: str, filename: str, module_package):
260297

261298
cache_filename = self.__get_cache_filename(filename, module_package)
@@ -273,14 +310,17 @@ def __download_module_asset(self, url: str, filename: str, module_package):
273310
logger.info(f"Using cached: {os.path.basename(zip_file)}")
274311
# Still emit some progress to show we're not stuck
275312
self.signalPackagingProgress.emit(-1.0, 0)
276-
return zip_file
313+
return zip_file, False
277314
except (zipfile.BadZipFile, OSError, Exception) as e:
278315
logger.warning(f"Existing file '{zip_file}' is invalid ({e}), will re-download")
279316
try:
280317
os.remove(zip_file)
281318
except OSError:
282319
pass
283320

321+
# Clean up old SHA-versioned files for the same asset
322+
self.__cleanup_old_cached_files(filename, cache_filename)
323+
284324
# Streaming, so we can iterate over the response.
285325
timeout = 60
286326
logger.info(f"Downloading: {os.path.basename(zip_file)}")
@@ -322,7 +362,7 @@ def __download_module_asset(self, url: str, filename: str, module_package):
322362
# Ensure final progress reflects completion
323363
self.__emit_progress(force=True)
324364

325-
return zip_file
365+
return zip_file, True
326366

327367
def __emit_progress(self, force: bool = False):
328368
"""Emit download progress as percentage (0-100) or -1 for indeterminate."""

oqtopus/gui/database_connection_widget.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,24 +223,29 @@ def refreshInstalledModules(self):
223223
beta_text = " \u26a0\ufe0f" if info["beta_testing"] else ""
224224
display = f"\u2022 <b>{module_label}</b> ({version}){beta_text}"
225225

226-
# Build tooltip with details
226+
# Build tooltip with details (rich HTML for bigger text)
227227
tooltip_lines = []
228-
tooltip_lines.append(f"Module: {module_label}")
229-
tooltip_lines.append(f"Schema: {schema}")
230-
tooltip_lines.append(f"Version: {version}")
228+
tooltip_lines.append(f"<b>Module:</b> {module_label}")
229+
tooltip_lines.append(f"<b>Schema:</b> {schema}")
230+
tooltip_lines.append(f"<b>Version:</b> {version}")
231231
if info["beta_testing"]:
232-
tooltip_lines.append("\u26a0\ufe0f Beta testing")
232+
tooltip_lines.append("\u26a0\ufe0f <b>Beta testing</b>")
233233
if info["installed_date"]:
234234
tooltip_lines.append(
235-
f"Installed: {info['installed_date'].strftime('%Y-%m-%d %H:%M')}"
235+
f"<b>Installed:</b> {info['installed_date'].strftime('%Y-%m-%d %H:%M')}"
236236
)
237237
if info["upgrade_date"]:
238238
tooltip_lines.append(
239-
f"Last upgrade: {info['upgrade_date'].strftime('%Y-%m-%d %H:%M')}"
239+
f"<b>Last upgrade:</b> {info['upgrade_date'].strftime('%Y-%m-%d %H:%M')}"
240240
)
241+
if info.get("parameters") and isinstance(info["parameters"], dict):
242+
tooltip_lines.append("<br><b>Parameters:</b>")
243+
for param_name, param_value in info["parameters"].items():
244+
tooltip_lines.append(f"&nbsp;&nbsp;{param_name} = {param_value}")
241245

246+
tooltip_html = "<p style='font-size:11pt'>" + "<br>".join(tooltip_lines) + "</p>"
242247
label = QLabel(display)
243-
label.setToolTip("\n".join(tooltip_lines))
248+
label.setToolTip(tooltip_html)
244249
layout.addWidget(label)
245250

246251
self.installed_modules_groupbox.setVisible(True)

oqtopus/gui/module_selection_widget.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,15 +267,21 @@ def __loadModuleFromZip(self, filename):
267267
def __packagePrepareTaskFinished(self):
268268
logger.info("Load package task finished")
269269

270-
self.module_progressBar.setVisible(False)
271-
272270
if isinstance(self.__packagePrepareTask.lastError, PackagePrepareTaskCanceled):
273271
logger.info("Load package task was canceled by user.")
274-
self.module_information_label.setText(self.tr("Package loading canceled."))
275-
QtUtils.setForegroundColor(self.module_information_label, PluginUtils.COLOR_WARNING)
272+
# A new task may already be running (user switched versions).
273+
# Only update UI if no new task has started.
274+
if not self.__packagePrepareTask.isRunning():
275+
self.module_progressBar.setVisible(False)
276+
self.module_information_label.setText(self.tr("Package loading canceled."))
277+
QtUtils.setForegroundColor(
278+
self.module_information_label, PluginUtils.COLOR_WARNING
279+
)
276280
# Don't emit signal_loadingFinished when cancelled - a new load may be starting
277281
return
278282

283+
self.module_progressBar.setVisible(False)
284+
279285
if self.__packagePrepareTask.lastError is not None:
280286
error_text = self.tr("Can't load module package:")
281287
CriticalMessageBox(

oqtopus/gui/module_widget.py

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from ..libs.pum.schema_migrations import SchemaMigrations
1414
from ..utils.plugin_utils import PluginUtils, logger
1515
from ..utils.qt_utils import CriticalMessageBox, QtUtils
16+
from .recreate_app_dialog import RecreateAppDialog
1617

1718
DIALOG_UI = PluginUtils.get_ui_class("module_widget.ui")
1819

@@ -217,7 +218,13 @@ def __packagePrepareGetPUMConfig(self):
217218
logger.info(f"PUM config loaded from '{pumConfigFilename}'")
218219

219220
try:
220-
self.parameters_groupbox.setParameters(self.__pum_config.parameters())
221+
all_params = self.__pum_config.parameters()
222+
standard_params = [p for p in all_params if not p.app_only]
223+
app_only_params = [p for p in all_params if p.app_only]
224+
self.parameters_groupbox.setParameters(standard_params)
225+
self.parameters_app_only_groupbox.setParameters(app_only_params)
226+
self.parameters_groupbox_upgrade.setParameters(standard_params)
227+
self.parameters_app_only_groupbox_upgrade.setParameters(app_only_params)
221228
except Exception as exception:
222229
CriticalMessageBox(
223230
self.tr("Error"),
@@ -266,7 +273,7 @@ def __installModuleClicked(self):
266273
return
267274

268275
try:
269-
parameters = self.parameters_groupbox.parameters_values()
276+
parameters = self.__get_all_parameters()
270277

271278
beta_testing = self.beta_testing_checkbox_pageInstall.isChecked()
272279
if beta_testing:
@@ -383,7 +390,7 @@ def __upgradeModuleClicked(self):
383390
return
384391

385392
try:
386-
parameters = self.parameters_groupbox.parameters_values()
393+
parameters = self.__get_all_parameters()
387394

388395
beta_testing = self.beta_testing_checkbox_pageUpgrade.isChecked()
389396
if beta_testing:
@@ -485,7 +492,7 @@ def __uninstallModuleClicked(self):
485492
return
486493

487494
try:
488-
parameters = self.parameters_groupbox.parameters_values()
495+
parameters = self.__get_all_parameters()
489496

490497
# Start background uninstall operation
491498
self.__startOperation("uninstall", parameters, {})
@@ -517,7 +524,7 @@ def __rolesClicked(self):
517524
return
518525

519526
try:
520-
parameters = self.parameters_groupbox.parameters_values()
527+
parameters = self.__get_all_parameters()
521528

522529
# Start background roles operation
523530
self.__startOperation("roles", parameters, {})
@@ -563,7 +570,7 @@ def __dropAppClicked(self):
563570
return
564571

565572
try:
566-
parameters = self.parameters_groupbox.parameters_values()
573+
parameters = self.__get_all_parameters()
567574

568575
# Start background drop app operation
569576
self.__startOperation("drop_app", parameters, {})
@@ -594,22 +601,22 @@ def __recreateAppClicked(self):
594601
).exec()
595602
return
596603

597-
reply = QMessageBox.question(
598-
self,
599-
self.tr("(Re)create app"),
600-
self.tr(
601-
"Are you sure you want to recreate the application?\n\n"
602-
"This will first drop the app and then create it again, executing the corresponding handlers."
603-
),
604-
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
605-
QMessageBox.StandardButton.No,
606-
)
604+
try:
605+
all_params = self.__pum_config.parameters()
606+
standard_params = [p for p in all_params if not p.app_only]
607+
app_only_params = [p for p in all_params if p.app_only]
608+
except Exception as exception:
609+
CriticalMessageBox(
610+
self.tr("Error"), self.tr("Can't load parameters:"), exception, self
611+
).exec()
612+
return
607613

608-
if reply != QMessageBox.StandardButton.Yes:
614+
dialog = RecreateAppDialog(standard_params, app_only_params, self)
615+
if dialog.exec() != RecreateAppDialog.DialogCode.Accepted:
609616
return
610617

611618
try:
612-
parameters = self.parameters_groupbox.parameters_values()
619+
parameters = dialog.parameters()
613620

614621
# Start background recreate app operation
615622
self.__startOperation("recreate_app", parameters, {})
@@ -620,6 +627,24 @@ def __recreateAppClicked(self):
620627
).exec()
621628
return
622629

630+
def __get_all_parameters(self) -> dict:
631+
"""Collect parameter values from both standard and app_only groupboxes.
632+
633+
Uses the upgrade-specific groupboxes when on the upgrade page,
634+
otherwise uses the install page groupboxes.
635+
"""
636+
values = {}
637+
if (
638+
self.moduleInfo_stackedWidget.currentWidget()
639+
== self.moduleInfo_stackedWidget_pageUpgrade
640+
):
641+
values.update(self.parameters_groupbox_upgrade.parameters_values())
642+
values.update(self.parameters_app_only_groupbox_upgrade.parameters_values())
643+
else:
644+
values.update(self.parameters_groupbox.parameters_values())
645+
values.update(self.parameters_app_only_groupbox.parameters_values())
646+
return values
647+
623648
def __show_error_state(self, message: str, on_label=None):
624649
"""Display an error state and hide the widget content."""
625650
label = on_label or self.moduleInfo_selected_label
@@ -653,6 +678,10 @@ def __show_install_page(self, version: str):
653678
# Configure beta testing checkbox based on package source
654679
self.__configure_beta_testing_checkbox(self.beta_testing_checkbox_pageInstall)
655680

681+
# On install, both standard and app_only parameters are editable
682+
self.parameters_groupbox.setEnabled(True)
683+
self.parameters_app_only_groupbox.setEnabled(True)
684+
656685
self.moduleInfo_stackedWidget.setCurrentWidget(self.moduleInfo_stackedWidget_pageInstall)
657686
# Ensure the stacked widget is visible when showing a valid page
658687
self.moduleInfo_stackedWidget.setVisible(True)
@@ -727,6 +756,10 @@ def __show_upgrade_page(
727756
# Configure beta testing checkbox based on package source
728757
self.__configure_beta_testing_checkbox(self.beta_testing_checkbox_pageUpgrade)
729758

759+
# On upgrade, standard parameters cannot be changed but remain scrollable
760+
self.parameters_groupbox_upgrade.setParametersEnabled(False)
761+
self.parameters_app_only_groupbox_upgrade.setParametersEnabled(True)
762+
730763
self.moduleInfo_stackedWidget.setCurrentWidget(self.moduleInfo_stackedWidget_pageUpgrade)
731764
self.moduleInfo_stackedWidget.setVisible(True)
732765

0 commit comments

Comments
 (0)