Skip to content

Commit

Permalink
Refactor download file to be simpler and handle error better.
Browse files Browse the repository at this point in the history
  • Loading branch information
ismailsunni committed Sep 12, 2024
1 parent 1cb8a90 commit cf907d0
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 256 deletions.
5 changes: 3 additions & 2 deletions qgis_hub_plugin/core/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


def get_all_resources(force_update=False):
print("get_all_resources", force_update)
# Check if the response file exits
response_folder = Path(QgsApplication.qgisSettingsDirPath(), "qgis_hub")
response_folder.mkdir(parents=True, exist_ok=True)
Expand All @@ -20,8 +21,8 @@ def get_all_resources(force_update=False):
# TODO: download in the background
# hardcoded to get all resource, currently only ~160
url = f"{BASE_URL}?limit=1000&format=json"

if download_file(url=url, file_path=response_file, force=force_update):
status = download_file(url=url, destination=response_file, force=force_update)
if status and response_file.exists():
with open(response_file) as f:
return json.load(f)
else:
Expand Down
70 changes: 25 additions & 45 deletions qgis_hub_plugin/gui/resource_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
download_file,
download_resource_thumbnail,
)
from qgis_hub_plugin.utilities.exception import DownloadError
from qgis_hub_plugin.utilities.qgis_util import show_busy_cursor

UI_CLASS = uic.loadUiType(
Expand Down Expand Up @@ -219,6 +220,7 @@ def restore_setting(self):

@show_busy_cursor
def populate_resources(self, force_update=False):
self.log(f"Populating resources {force_update}")
if force_update or not self.resources:
response = get_all_resources(force_update=force_update)

Expand Down Expand Up @@ -394,11 +396,9 @@ def download_resource(self):
if not file_path.endswith(file_extension):
file_path = file_path + file_extension

if not download_resource_file(resource.file, file_path):
self.show_warning_message(f"Download failed for {resource.name}")
else:
try:
download_file(resource.file, Path(file_path))
self.show_success_message(f"Downloaded {resource.name} to {file_path}")

if self.checkBoxOpenDirectory.isChecked():
QDesktopServices.openUrl(
QUrl.fromLocalFile(str(Path(file_path).parent))
Expand All @@ -407,16 +407,21 @@ def download_resource(self):
self.plg_settings.set_value_from_key(
"download_location", str(Path(file_path).parent)
)
except DownloadError as e:
self.show_error_message(str(e))

def add_resource_to_qgis(self):
if self.selected_resource.resource_type == ResoureType.Model:
self.add_model_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Style:
self.add_style_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Geopackage:
self.add_geopackage_to_qgis()
elif self.selected_resource.resource_type == ResoureType.LayerDefinition:
self.add_layer_definition_to_qgis()
try:
if self.selected_resource.resource_type == ResoureType.Model:
self.add_model_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Style:
self.add_style_to_qgis()
elif self.selected_resource.resource_type == ResoureType.Geopackage:
self.add_geopackage_to_qgis()
elif self.selected_resource.resource_type == ResoureType.LayerDefinition:
self.add_layer_definition_to_qgis()
except DownloadError as e:
self.show_error_message(str(e))

@show_busy_cursor
def add_model_to_qgis(self):
Expand All @@ -428,23 +433,12 @@ def add_model_to_qgis(self):
if not custom_model_directory.exists():
custom_model_directory.mkdir(parents=True, exist_ok=True)

file_path = os.path.join(
custom_model_directory, os.path.basename(resource.file)
)
file_path = custom_model_directory / os.path.basename(resource.file)

if download_resource_file(resource.file, file_path):
# Refreshing the processing toolbox
QgsApplication.processingRegistry().providerById(
"model"
).refreshAlgorithms()
self.show_success_message(
self.tr(f"Model {resource.name} is added to QGIS")
)

else:
self.show_warning_message(
self.tr(f"Download failed for model {resource.name}")
)
download_file(resource.file, file_path)
# Refreshing the processing toolbox
QgsApplication.processingRegistry().providerById("model").refreshAlgorithms()
self.show_success_message(self.tr(f"Model {resource.name} is added to QGIS"))

@show_busy_cursor
def add_geopackage_to_qgis(self):
Expand All @@ -467,9 +461,7 @@ def add_geopackage_to_qgis(self):
if not file_path.endswith(file_extension):
file_path = file_path + file_extension

if not download_resource_file(resource.file, file_path):
self.show_error_message(self.tr(f"Download failed for {resource.name}"))
return
download_file(resource.file, file_path)

extract_location = Path(os.path.dirname(file_path))
current_project = QgsProject.instance()
Expand Down Expand Up @@ -551,7 +543,7 @@ def add_style_to_qgis(self):
"/tmp" if platform.system() == "Darwin" else tempfile.gettempdir()
)
tempfile_path = Path(tempdir, resource.file.split("/")[-1])
download_resource_file(resource.file, tempfile_path, True)
download_file(resource.file, tempfile_path, True)

# Add to QGIS style library
style = QgsStyle().defaultStyle()
Expand All @@ -575,9 +567,7 @@ def add_layer_definition_to_qgis(self):
if not layer_definition_dir.exists():
layer_definition_dir.mkdir(parents=True, exist_ok=True)

if not download_resource_file(resource.file, file_path):
self.show_error_message(self.tr(f"Download failed for {resource.name}"))
return
download_file(resource.file, file_path)

current_project = QgsProject.instance()

Expand Down Expand Up @@ -635,13 +625,3 @@ def resize_columns(self):

def update_icon_size(self, size):
self.listViewResources.setIconSize(QSize(size, size))


# TODO: do it QGIS task to have
def download_resource_file(url: str, file_path: str, force: bool = False):
resource_path = Path(file_path)
download_file(url, resource_path, force)
if resource_path.exists():
return resource_path
else:
return None
74 changes: 62 additions & 12 deletions qgis_hub_plugin/utilities/common.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import os
from pathlib import Path
from typing import Optional

from qgis.core import QgsApplication
from qgis.core import QgsApplication, QgsNetworkAccessManager, QgsNetworkReplyContent
from qgis.PyQt.QtCore import QFile, QIODevice, QUrl
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtNetwork import QNetworkReply, QNetworkRequest

from qgis_hub_plugin.__about__ import DIR_PLUGIN_ROOT
from qgis_hub_plugin.toolbelt import PlgLogger
from qgis_hub_plugin.utilities.file_downloader import FileDownloader
from qgis_hub_plugin.utilities.exception import DownloadError

QGIS_HUB_DIR = Path(QgsApplication.qgisSettingsDirPath(), "qgis_hub")

Expand All @@ -20,16 +23,63 @@ def get_icon_path(icon_name: str) -> str:
return os.path.join(DIR_PLUGIN_ROOT, "resources", "images", icon_name)


def download_file(url: str, file_path: Path, force: bool = False):
if not force and file_path.exists():
return
downloader = FileDownloader(url, str(file_path.absolute()))
# def download_file(url: str, file_path: Path, force: bool = False):
# if not force and file_path.exists():
# return


def download_file(
url: str, destination: Path, force: bool = True, timeout: int = 30000
) -> Optional[str]:
"""
Download a file from the given URL to the specified destination using PyQGIS.
Args:
url (str): The URL of the file to download.
destination (Path): The local path where the file should be saved.
timeout (int): The timeout for the request in milliseconds. Defaults to 30000 (30 seconds).
Returns:
Optional[Path]: The path to the downloaded file if successful, None otherwise.
Raises:
DownloadError: If any error occurs during the download process.
"""
if not force and destination.exists():
return destination
nam = QgsNetworkAccessManager.instance()
request = QNetworkRequest(QUrl(url))
request.setTransferTimeout(timeout)

def handle_finished(reply: QgsNetworkReplyContent):
if reply.error() == QNetworkReply.NoError:
file = QFile(str(destination))
if file.open(QIODevice.WriteOnly):
file.write(reply.readAll())
file.close()
return destination
else:
raise DownloadError(
f"Failed to open file for writing: {file.errorString()}"
)
elif reply.error() == QNetworkReply.ContentNotFoundError:
raise DownloadError(f"File not found (404 error): {url}")
else:
raise DownloadError(f"Download failed: {reply.errorString()}")

try:
result, message = downloader.download()
except OSError as ex:
raise OSError(ex)
reply = nam.get(request)

# Use a loop to process events and prevent GUI freezing
while not reply.isFinished():
QgsApplication.processEvents()

return file_path.exists()
return handle_finished(reply)
except Exception as e:
if isinstance(e, DownloadError):
raise e
else:
raise DownloadError(f"An unexpected error occurred: {str(e)}")


# If not able to download or not found, set the thumbnail to the default one
Expand All @@ -54,8 +104,8 @@ def download_resource_thumbnail(url: str, uuid: str) -> Path:
if not thumbnail_dir.exists():
thumbnail_dir.mkdir(parents=True, exist_ok=True)

download_file(url, thumbnail_path)
if thumbnail_path.exists():
status = download_file(url, thumbnail_path, False)
if status and thumbnail_path.exists():
return thumbnail_path
else:
return Path(get_icon_path("QGIS_Hub_icon.svg"))
2 changes: 2 additions & 0 deletions qgis_hub_plugin/utilities/exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class DownloadError(Exception):
pass
Loading

0 comments on commit cf907d0

Please sign in to comment.