Skip to content

Commit 2d5c015

Browse files
committed
ref: move logic for downloading collections/datasets/resources to download widget
1 parent cffcb94 commit 2d5c015

7 files changed

Lines changed: 85 additions & 79 deletions

File tree

dcoraid/gui/dbview/filter_base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
class FilterBase(QtWidgets.QWidget):
88
#: The user selection has changed
99
selection_changed = QtCore.pyqtSignal(list)
10-
download_resource = QtCore.pyqtSignal(str, bool)
10+
#: Download something (which, id_or_name, condensed)
11+
#: - `which` is e.g. "resource", "dataset", "collection", circle
12+
#: - `id_or_name` is the identifier or name of the thing
13+
#: - `condensed` determines whether to download condensed resources only
14+
download_item = QtCore.pyqtSignal(str, str, bool)
1115

1216
def __init__(self, *args, **kwargs):
1317
"""Filter view widget with title, edit, checkbox, and table

dcoraid/gui/dbview/filter_chain.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212

1313
class FilterChain(QtWidgets.QWidget):
14-
download_resource = QtCore.pyqtSignal(str, bool)
14+
download_item = QtCore.pyqtSignal(str, str, bool)
1515

1616
def __init__(self, *args, **kwargs):
1717
"""Filter chain widget with multiple filter views
@@ -35,10 +35,10 @@ def __init__(self, *args, **kwargs):
3535
# resource filtered by .rtdc
3636
self.fw_resources.checkBox.stateChanged.connect(self.update_resources)
3737
# request resource downloads
38-
self.fw_circles.download_resource.connect(self.download_resource)
39-
self.fw_collections.download_resource.connect(self.download_resource)
40-
self.fw_datasets.download_resource.connect(self.download_resource)
41-
self.fw_resources.download_resource.connect(self.download_resource)
38+
self.fw_circles.download_item.connect(self.download_item)
39+
self.fw_collections.download_item.connect(self.download_item)
40+
self.fw_datasets.download_item.connect(self.download_item)
41+
self.fw_resources.download_item.connect(self.download_item)
4242

4343
@property
4444
def selected_circles(self):

dcoraid/gui/dbview/filter_views.py

Lines changed: 14 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
from functools import partial
22
import webbrowser
33

4-
from PyQt6 import QtCore, QtWidgets
4+
from PyQt6 import QtWidgets
55
from PyQt6.QtCore import Qt
66

77
from ...common import is_dc_resource_dict
88

99
from ..api import get_ckan_api
10-
from ..tools import ShowWaitCursor
1110

1211
from . import filter_base
1312

@@ -39,47 +38,18 @@ def __init__(self, *args, **kwargs):
3938
self.checkBox.setVisible(False)
4039
self.label_info.setVisible(False)
4140

42-
def download_collection(self, collection_name, condensed=False):
43-
"""Emit signals to download all resources of a collection
44-
45-
Parameters
46-
----------
47-
collection_name: str
48-
Name of the collection
49-
condensed: bool
50-
Whether to download condensed resources instead
51-
"""
52-
with ShowWaitCursor():
53-
api = get_ckan_api()
54-
search_dict = api.get("package_search",
55-
fq=f"+groups:{collection_name}",
56-
include_private=True,
57-
rows=1000)
58-
num_datasets = search_dict["count"]
59-
if num_datasets >= 1000:
60-
raise NotImplementedError(
61-
# We have to increase ckan.search.rows_max = 1000
62-
# or (better) use the `start` parameter until we
63-
# hit a number < 1000.
64-
f"There are too many datasets in '{collection_name}'!")
65-
for ii, ds_dict in enumerate(search_dict["results"]):
66-
for res_dict in ds_dict.get("resources", []):
67-
self.download_resource.emit(res_dict["id"], condensed)
68-
QtWidgets.QApplication.processEvents(
69-
QtCore.QEventLoop.ProcessEventsFlag.AllEvents,
70-
300)
71-
7241
def get_entry_actions(self, row, entry):
7342
api = get_ckan_api()
7443
url = f"{api.server}/group/{entry['name']}"
7544
actions = [
7645
{"icon": "angle-down",
7746
"tooltip": f"download collection {entry['name']}",
78-
"function": partial(self.download_collection, entry["name"])},
47+
"function": partial(self.download_item.emit,
48+
"collection", entry["name"], False)},
7949
{"icon": "angles-down",
8050
"tooltip": f"download condensed collection {entry['name']}",
81-
"function": partial(self.download_collection, entry["name"],
82-
condensed=True)},
51+
"function": partial(self.download_item.emit,
52+
"collection", entry["name"], True)},
8353
{"icon": "eye",
8454
"tooltip": f"view collection {entry['name']} online",
8555
"function": partial(webbrowser.open, url)}
@@ -95,35 +65,18 @@ def __init__(self, *args, **kwargs):
9565
self.checkBox.setVisible(False)
9666
self.label_info.setVisible(False)
9767

98-
def download_dataset(self, dataset_id, condensed=False):
99-
"""Emit signals to download all resources of a dataset
100-
101-
Parameters
102-
----------
103-
dataset_id: str
104-
dataset ID
105-
condensed: bool
106-
Whether to download condensed resources instead
107-
"""
108-
api = get_ckan_api()
109-
ds_dict = api.get("package_show", id=dataset_id)
110-
for res_dict in ds_dict.get("resources", []):
111-
self.download_resource.emit(res_dict["id"], condensed)
112-
QtWidgets.QApplication.processEvents(
113-
QtCore.QEventLoop.ProcessEventsFlag.AllEvents,
114-
300)
115-
11668
def get_entry_actions(self, row, entry):
11769
api = get_ckan_api()
11870
url = f"{api.server}/dataset/{entry['name']}"
11971
actions = [
12072
{"icon": "angle-down",
12173
"tooltip": f"download dataset {entry['name']}",
122-
"function": partial(self.download_dataset, entry["id"])},
74+
"function": partial(self.download_item.emit,
75+
"dataset", entry["id"], False)},
12376
{"icon": "angles-down",
12477
"tooltip": f"download condensed dataset {entry['name']}",
125-
"function": partial(self.download_dataset, entry["id"],
126-
condensed=True)},
78+
"function": partial(self.download_item.emit,
79+
"dataset", entry["id"], True)},
12780
{"icon": "eye",
12881
"tooltip": f"view dataset {entry['name']} online",
12982
"function": partial(webbrowser.open, url)},
@@ -153,8 +106,8 @@ def get_entry_actions(self, row, entry):
153106
actions = [
154107
{"icon": "angle-down",
155108
"tooltip": f"download resource {entry['name']}",
156-
"function": partial(self.download_resource.emit,
157-
entry["id"], False)},
109+
"function": partial(self.download_item.emit,
110+
"resource", entry["id"], False)},
158111
{"icon": "eye",
159112
"tooltip": f"view resource {entry['name']} online",
160113
"function": partial(webbrowser.open, url)},
@@ -165,8 +118,9 @@ def get_entry_actions(self, row, entry):
165118
1,
166119
{"icon": "angles-down",
167120
"tooltip": f"download condensed resource {entry['name']}",
168-
"function": partial(self.download_resource.emit,
169-
entry["id"], True)},)
121+
"function": partial(self.download_item.emit,
122+
"resource", entry["id"], True)},
123+
)
170124
return actions
171125

172126
def set_entry_label(self, row, entry):

dcoraid/gui/main.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ def __init__(self, *args, **kwargs):
104104
self.status_widget.clicked.connect(self.dlg_pref.on_show_server)
105105

106106
# Signal for requesting resource download
107-
self.panel_find_data.request_download.connect(
108-
self.panel_download.download_resource)
109-
self.panel_my_data.request_download.connect(
110-
self.panel_download.download_resource)
107+
self.panel_find_data.download_item.connect(
108+
self.panel_download.download_an_item)
109+
self.panel_my_data.download_item.connect(
110+
self.panel_download.download_an_item)
111111

112112
# Signal for dataset upload or modification
113113
self.panel_upload.upload_finished.connect(self.on_dataset_changed)
@@ -151,6 +151,7 @@ def __init__(self, *args, **kwargs):
151151
else:
152152
self.panel_find_data.set_database(self.database)
153153
self.panel_my_data.set_database(self.database)
154+
self.panel_download.set_database(self.database)
154155

155156
self.status_widget.request_status_update()
156157

dcoraid/gui/panel_downloads/widget_download.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from PyQt6 import uic, QtCore, QtGui, QtWidgets
1313
from PyQt6.QtCore import QStandardPaths
1414

15+
from ...common import is_dc_resource_dict
1516
from ...download import DownloadQueue
1617

1718
from ..api import get_ckan_api
@@ -33,6 +34,8 @@ def __init__(self, *args, **kwargs):
3334
with resources.as_file(ref_ui) as path_ui:
3435
uic.loadUi(path_ui, self)
3536

37+
self.database = None
38+
3639
self.settings = QtCore.QSettings()
3740

3841
#: path to persistent shelf to be able to resume uploads on startup
@@ -83,11 +86,49 @@ def initialize(self):
8386
self.init_timer.setInterval(1000)
8487
self.init_timer.start()
8588

89+
@QtCore.pyqtSlot(str, str, bool)
90+
def download_an_item(self, which, identifier, condensed=False):
91+
if which == "collection":
92+
self.download_collection(identifier, condensed)
93+
elif which == "dataset":
94+
self.download_dataset(identifier, condensed)
95+
elif which == "resource":
96+
self.download_resource(identifier, condensed)
97+
else:
98+
raise ValueError(f"Invalid download item specified: {which}")
99+
100+
@QtCore.pyqtSlot(str, bool)
101+
def download_collection(self, collection_id, condensed=False):
102+
"""Download all resources from all datasets in a collection"""
103+
# Get all datasets in that collection
104+
api = get_ckan_api()
105+
ds_list = api.get("package_search",
106+
fq=f"+groups:{collection_id}",
107+
include_private=True,
108+
rows=1000)["results"]
109+
for ds_dict in ds_list:
110+
# use this opportunity to update our database
111+
self.database.update_dataset(ds_dict)
112+
# send this dataset on for downloading
113+
self.download_dataset(ds_dict["id"], condensed)
114+
115+
@QtCore.pyqtSlot(str, bool)
116+
def download_dataset(self, dataset_id, condensed=False):
117+
"""Download all resources in a dataset"""
118+
dl_path = self.get_download_path()
119+
ds_dict = self.database.get_dataset_dict(dataset_id)
120+
if self.jobs is not None:
121+
for res_dict in ds_dict.get("resources", []):
122+
cond_this = condensed
123+
if condensed and not is_dc_resource_dict(res_dict):
124+
# Not a DC resource; cannot download condensed file!
125+
cond_this = False
126+
self.jobs.new_job(res_dict["id"], dl_path, cond_this)
127+
86128
@QtCore.pyqtSlot(str, bool)
87129
def download_resource(self, resource_id, condensed=False):
88-
fallback = QStandardPaths.writableLocation(
89-
QStandardPaths.StandardLocation.DownloadLocation)
90-
dl_path = self.settings.value("downloads/default path", fallback)
130+
"""Download a resource"""
131+
dl_path = self.get_download_path()
91132
if self.jobs is not None:
92133
self.jobs.new_job(resource_id, dl_path, condensed)
93134
else:
@@ -98,6 +139,11 @@ def download_resource(self, resource_id, condensed=False):
98139
f"We cannot connect to '{api.server}'. Please make "
99140
f"sure you are connected to the network.")
100141

142+
def get_download_path(self):
143+
fallback = QStandardPaths.writableLocation(
144+
QStandardPaths.StandardLocation.DownloadLocation)
145+
return self.settings.value("downloads/default path", fallback)
146+
101147
def prepare_quit(self):
102148
"""Should be called before the application quits"""
103149
self.init_timer.stop()
@@ -106,6 +152,9 @@ def prepare_quit(self):
106152
if self.jobs is not None:
107153
self.jobs.__del__()
108154

155+
def set_database(self, database):
156+
self.database = database
157+
109158

110159
class DownloadTableWidget(QtWidgets.QTableWidget):
111160
download_finished = QtCore.pyqtSignal()

dcoraid/gui/panel_find_data/widget_find_data.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313

1414
class WidgetFindData(QtWidgets.QWidget):
15-
request_download = QtCore.pyqtSignal(str, bool)
15+
download_item = QtCore.pyqtSignal(str, str, bool)
1616

1717
def __init__(self, *args, **kwargs):
1818
"""Browse DCOR data"""
@@ -32,8 +32,7 @@ def __init__(self, *args, **kwargs):
3232
# Signals for data browser
3333
self.pushButton_search.clicked.connect(self.on_search)
3434
self.pushButton_update_db.clicked.connect(self.on_update_db)
35-
self.public_filter_chain.download_resource.connect(
36-
self.request_download)
35+
self.public_filter_chain.download_item.connect(self.download_item)
3736

3837
@QtCore.pyqtSlot()
3938
def on_search(self):

dcoraid/gui/panel_my_data/widget_my_data.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717

1818
class WidgetMyData(QtWidgets.QWidget):
19-
request_download = QtCore.pyqtSignal(str, bool)
19+
download_item = QtCore.pyqtSignal(str, str, bool)
2020

2121
def __init__(self, *args, **kwargs):
2222
"""Browse public DCOR data"""
@@ -29,8 +29,7 @@ def __init__(self, *args, **kwargs):
2929
# Signals for user datasets (my data)
3030
self.pushButton_user_refresh.clicked.connect(
3131
self.on_refresh_private_data)
32-
self.user_filter_chain.download_resource.connect(
33-
self.request_download)
32+
self.user_filter_chain.download_item.connect(self.download_item)
3433
self.user_filter_chain.added_datasets_to_collection.connect(
3534
self.on_added_datasets_to_collection)
3635

0 commit comments

Comments
 (0)