data-sourcerer 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data_sourcerer-0.1.0.dist-info/METADATA +52 -0
- data_sourcerer-0.1.0.dist-info/RECORD +95 -0
- data_sourcerer-0.1.0.dist-info/WHEEL +5 -0
- data_sourcerer-0.1.0.dist-info/entry_points.txt +2 -0
- data_sourcerer-0.1.0.dist-info/licenses/LICENSE +21 -0
- data_sourcerer-0.1.0.dist-info/top_level.txt +1 -0
- sourcerer/__init__.py +15 -0
- sourcerer/domain/__init__.py +13 -0
- sourcerer/domain/access_credentials/__init__.py +7 -0
- sourcerer/domain/access_credentials/entities.py +53 -0
- sourcerer/domain/access_credentials/exceptions.py +17 -0
- sourcerer/domain/access_credentials/repositories.py +84 -0
- sourcerer/domain/access_credentials/services.py +91 -0
- sourcerer/domain/file_system/__init__.py +7 -0
- sourcerer/domain/file_system/entities.py +70 -0
- sourcerer/domain/file_system/exceptions.py +17 -0
- sourcerer/domain/file_system/services.py +64 -0
- sourcerer/domain/shared/__init__.py +6 -0
- sourcerer/domain/shared/entities.py +18 -0
- sourcerer/domain/storage_provider/__init__.py +7 -0
- sourcerer/domain/storage_provider/entities.py +86 -0
- sourcerer/domain/storage_provider/exceptions.py +17 -0
- sourcerer/domain/storage_provider/services.py +130 -0
- sourcerer/infrastructure/__init__.py +13 -0
- sourcerer/infrastructure/access_credentials/__init__.py +7 -0
- sourcerer/infrastructure/access_credentials/exceptions.py +16 -0
- sourcerer/infrastructure/access_credentials/registry.py +120 -0
- sourcerer/infrastructure/access_credentials/repositories.py +119 -0
- sourcerer/infrastructure/access_credentials/services.py +396 -0
- sourcerer/infrastructure/db/__init__.py +6 -0
- sourcerer/infrastructure/db/config.py +73 -0
- sourcerer/infrastructure/db/models.py +47 -0
- sourcerer/infrastructure/file_system/__init__.py +7 -0
- sourcerer/infrastructure/file_system/exceptions.py +89 -0
- sourcerer/infrastructure/file_system/services.py +147 -0
- sourcerer/infrastructure/storage_provider/__init__.py +7 -0
- sourcerer/infrastructure/storage_provider/exceptions.py +78 -0
- sourcerer/infrastructure/storage_provider/registry.py +84 -0
- sourcerer/infrastructure/storage_provider/services.py +509 -0
- sourcerer/infrastructure/utils.py +106 -0
- sourcerer/presentation/__init__.py +12 -0
- sourcerer/presentation/app.py +36 -0
- sourcerer/presentation/di_container.py +46 -0
- sourcerer/presentation/screens/__init__.py +0 -0
- sourcerer/presentation/screens/critical_error/__init__.py +0 -0
- sourcerer/presentation/screens/critical_error/main.py +78 -0
- sourcerer/presentation/screens/critical_error/styles.tcss +41 -0
- sourcerer/presentation/screens/file_system_finder/main.py +248 -0
- sourcerer/presentation/screens/file_system_finder/styles.tcss +44 -0
- sourcerer/presentation/screens/file_system_finder/widgets/__init__.py +0 -0
- sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +810 -0
- sourcerer/presentation/screens/main/__init__.py +0 -0
- sourcerer/presentation/screens/main/main.py +469 -0
- sourcerer/presentation/screens/main/messages/__init__.py +0 -0
- sourcerer/presentation/screens/main/messages/delete_request.py +12 -0
- sourcerer/presentation/screens/main/messages/download_request.py +12 -0
- sourcerer/presentation/screens/main/messages/preview_request.py +10 -0
- sourcerer/presentation/screens/main/messages/resizing_rule.py +21 -0
- sourcerer/presentation/screens/main/messages/select_storage_item.py +11 -0
- sourcerer/presentation/screens/main/messages/uncheck_files_request.py +8 -0
- sourcerer/presentation/screens/main/messages/upload_request.py +10 -0
- sourcerer/presentation/screens/main/mixins/__init__.py +0 -0
- sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +144 -0
- sourcerer/presentation/screens/main/styles.tcss +32 -0
- sourcerer/presentation/screens/main/widgets/__init__.py +0 -0
- sourcerer/presentation/screens/main/widgets/gradient.py +45 -0
- sourcerer/presentation/screens/main/widgets/resizing_rule.py +67 -0
- sourcerer/presentation/screens/main/widgets/storage_content.py +691 -0
- sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +143 -0
- sourcerer/presentation/screens/preview_content/__init__.py +0 -0
- sourcerer/presentation/screens/preview_content/main.py +59 -0
- sourcerer/presentation/screens/preview_content/styles.tcss +26 -0
- sourcerer/presentation/screens/provider_creds_list/__init__.py +0 -0
- sourcerer/presentation/screens/provider_creds_list/main.py +164 -0
- sourcerer/presentation/screens/provider_creds_list/styles.tcss +65 -0
- sourcerer/presentation/screens/provider_creds_registration/__init__.py +0 -0
- sourcerer/presentation/screens/provider_creds_registration/main.py +264 -0
- sourcerer/presentation/screens/provider_creds_registration/styles.tcss +42 -0
- sourcerer/presentation/screens/question/__init__.py +0 -0
- sourcerer/presentation/screens/question/main.py +31 -0
- sourcerer/presentation/screens/question/styles.tcss +33 -0
- sourcerer/presentation/screens/shared/__init__.py +0 -0
- sourcerer/presentation/screens/shared/containers.py +13 -0
- sourcerer/presentation/screens/shared/widgets/__init__.py +0 -0
- sourcerer/presentation/screens/shared/widgets/button.py +54 -0
- sourcerer/presentation/screens/shared/widgets/labeled_input.py +80 -0
- sourcerer/presentation/screens/storage_action_progress/__init__.py +0 -0
- sourcerer/presentation/screens/storage_action_progress/main.py +476 -0
- sourcerer/presentation/screens/storage_action_progress/styles.tcss +43 -0
- sourcerer/presentation/settings.py +31 -0
- sourcerer/presentation/themes/__init__.py +0 -0
- sourcerer/presentation/themes/github_dark.py +21 -0
- sourcerer/presentation/utils.py +69 -0
- sourcerer/settings.py +72 -0
- sourcerer/utils.py +32 -0
File without changes
|
@@ -0,0 +1,469 @@
|
|
1
|
+
import traceback
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from textual import work, on
|
5
|
+
from textual.app import App, ComposeResult
|
6
|
+
from textual.binding import Binding
|
7
|
+
from textual.containers import Horizontal
|
8
|
+
from textual.reactive import reactive
|
9
|
+
from textual.widgets import Footer
|
10
|
+
|
11
|
+
from sourcerer.infrastructure.access_credentials.services import CredentialsService
|
12
|
+
from sourcerer.infrastructure.storage_provider.exceptions import (
|
13
|
+
ListStorageItemsException,
|
14
|
+
)
|
15
|
+
from sourcerer.infrastructure.utils import generate_uuid
|
16
|
+
from sourcerer.presentation.screens.critical_error.main import CriticalErrorScreen
|
17
|
+
from sourcerer.presentation.screens.file_system_finder.main import (
|
18
|
+
FileSystemNavigationModal,
|
19
|
+
)
|
20
|
+
from sourcerer.presentation.screens.main.messages.delete_request import DeleteRequest
|
21
|
+
from sourcerer.presentation.screens.main.messages.download_request import (
|
22
|
+
DownloadRequest,
|
23
|
+
)
|
24
|
+
from sourcerer.presentation.screens.main.messages.preview_request import PreviewRequest
|
25
|
+
from sourcerer.presentation.screens.main.messages.select_storage_item import (
|
26
|
+
SelectStorageItem,
|
27
|
+
)
|
28
|
+
from sourcerer.presentation.screens.main.messages.uncheck_files_request import (
|
29
|
+
UncheckFilesRequest,
|
30
|
+
)
|
31
|
+
from sourcerer.presentation.screens.main.messages.upload_request import UploadRequest
|
32
|
+
from sourcerer.presentation.screens.main.mixins.resize_containers_watcher_mixin import (
|
33
|
+
ResizeContainersWatcherMixin,
|
34
|
+
)
|
35
|
+
from sourcerer.presentation.screens.main.widgets.resizing_rule import ResizingRule
|
36
|
+
from sourcerer.presentation.screens.main.widgets.storage_content import (
|
37
|
+
StorageContentContainer,
|
38
|
+
)
|
39
|
+
from sourcerer.presentation.screens.main.widgets.storage_list_sidebar import (
|
40
|
+
StorageListSidebar,
|
41
|
+
)
|
42
|
+
from sourcerer.presentation.screens.preview_content.main import PreviewContentScreen
|
43
|
+
from sourcerer.presentation.screens.provider_creds_list.main import (
|
44
|
+
ProviderCredsListScreen,
|
45
|
+
)
|
46
|
+
from sourcerer.presentation.screens.storage_action_progress.main import (
|
47
|
+
StorageActionProgressScreen,
|
48
|
+
UploadKey,
|
49
|
+
DownloadKey,
|
50
|
+
DeleteKey,
|
51
|
+
)
|
52
|
+
from sourcerer.presentation.themes.github_dark import github_dark_theme
|
53
|
+
from sourcerer.presentation.utils import (
|
54
|
+
get_provider_service_by_access_uuid,
|
55
|
+
get_provider_service_by_access_credentials,
|
56
|
+
)
|
57
|
+
|
58
|
+
|
59
|
+
class Sourcerer(App, ResizeContainersWatcherMixin):
|
60
|
+
"""
|
61
|
+
A Textual application for managing cloud storage credentials and content.
|
62
|
+
|
63
|
+
This application provides a user interface to list and manage cloud storage
|
64
|
+
credentials, view storage content, and handle storage item selection. It
|
65
|
+
integrates with various cloud storage providers and supports asynchronous
|
66
|
+
operations for fetching and displaying storage data.
|
67
|
+
|
68
|
+
Attributes:
|
69
|
+
CSS_PATH (str): Path to the CSS file for styling the application.
|
70
|
+
BINDINGS (list): Key bindings for application actions.
|
71
|
+
is_storage_list_loading (reactive): Reactive attribute indicating if the
|
72
|
+
storage list is currently loading.
|
73
|
+
|
74
|
+
Methods:
|
75
|
+
compose() -> ComposeResult: Composes the UI layout with storage list and
|
76
|
+
content areas.
|
77
|
+
on_mount(): Initializes the application theme and storage list on mount.
|
78
|
+
action_credentials_list(): Opens the credentials list screen and refreshes
|
79
|
+
storages.
|
80
|
+
refresh_storages(*args, **kwargs): Refreshes the storage list by clearing
|
81
|
+
and reinitializing it.
|
82
|
+
init_storages_list() -> None: Asynchronously initializes the storage list
|
83
|
+
by fetching and displaying storages.
|
84
|
+
on_select_storage_item(event: SelectStorageItem): Handles storage item
|
85
|
+
selection and updates storage content.
|
86
|
+
"""
|
87
|
+
|
88
|
+
CSS_PATH = "styles.tcss"
|
89
|
+
BINDINGS = [Binding("ctrl+r", "registrations", "Registrations list")]
|
90
|
+
is_storage_list_loading = reactive(False, recompose=True)
|
91
|
+
|
92
|
+
def __init__(self, *args, **kwargs):
|
93
|
+
super().__init__(*args, **kwargs)
|
94
|
+
self.credentials_service = CredentialsService()
|
95
|
+
self.storage_list_sidebar = StorageListSidebar(id="storage_list_sidebar")
|
96
|
+
self.storage_content = StorageContentContainer(id="storage_content_container")
|
97
|
+
self.load_percentage = 0
|
98
|
+
self.active_resizing_rule: ResizingRule | None = None
|
99
|
+
|
100
|
+
def compose(self) -> ComposeResult:
|
101
|
+
with Horizontal(id="main"):
|
102
|
+
yield self.storage_list_sidebar
|
103
|
+
yield ResizingRule(
|
104
|
+
id="storage_list_sidebar_container",
|
105
|
+
orientation="vertical",
|
106
|
+
classes="resize-handle",
|
107
|
+
prev_component_id=self.storage_list_sidebar.id,
|
108
|
+
next_component_id=self.storage_content.id,
|
109
|
+
)
|
110
|
+
yield self.storage_content
|
111
|
+
yield Footer()
|
112
|
+
|
113
|
+
def _handle_exception(self, error: Exception) -> None:
|
114
|
+
self.push_screen(CriticalErrorScreen(str(error), traceback.format_exc()))
|
115
|
+
|
116
|
+
def on_mount(self):
|
117
|
+
"""
|
118
|
+
Initializes the application theme and storage list on mount.
|
119
|
+
"""
|
120
|
+
|
121
|
+
self.register_theme(github_dark_theme) # pyright: ignore [reportArgumentType]
|
122
|
+
|
123
|
+
self.theme = "github-dark"
|
124
|
+
self.init_storages_list()
|
125
|
+
|
126
|
+
def action_registrations(self):
|
127
|
+
"""
|
128
|
+
Opens the provider credentials list screen and refreshes the storage list.
|
129
|
+
This method is triggered by the key binding "ctrl+r" and allows the user
|
130
|
+
to manage their cloud storage credentials. It pushes the
|
131
|
+
ProviderCredsListScreen to the application stack and sets a callback
|
132
|
+
to refresh the storage list after the screen is closed.
|
133
|
+
This method is typically used to allow users to add their
|
134
|
+
cloud storage credentials, which will then be reflected in the storage
|
135
|
+
"""
|
136
|
+
self.app.push_screen(ProviderCredsListScreen(), callback=self.refresh_storages)
|
137
|
+
|
138
|
+
def refresh_storages(self, *args, **kwargs):
|
139
|
+
"""
|
140
|
+
Refreshes the storage list by clearing the current storages and
|
141
|
+
reinitializing the storages list. This method is typically used
|
142
|
+
to update the storage list after changes in credentials or storage
|
143
|
+
configurations.
|
144
|
+
"""
|
145
|
+
self.storage_list_sidebar.storages = {}
|
146
|
+
self.init_storages_list()
|
147
|
+
|
148
|
+
@work(thread=True)
|
149
|
+
async def init_storages_list(self) -> None:
|
150
|
+
"""
|
151
|
+
Initializes the list of storages by fetching active access credentials
|
152
|
+
and retrieving storages for each credential. Updates the storage list
|
153
|
+
sidebar with the retrieved storages. Notifies the user in case of errors.
|
154
|
+
|
155
|
+
This method is asynchronous and should be awaited.
|
156
|
+
|
157
|
+
Flow:
|
158
|
+
1. Fetch active access credentials using self.credentials_service.list.
|
159
|
+
2. If no credentials are found, exit the method.
|
160
|
+
3. For each credential, retrieve the corresponding provider service.
|
161
|
+
4. Attempt to list storages using the provider service.
|
162
|
+
5. Update the storage_list_sidebar with the retrieved storages.
|
163
|
+
6. Handle exceptions by printing an error message and notifying the user.
|
164
|
+
"""
|
165
|
+
self.reset_storage_content()
|
166
|
+
access_credentials = self.credentials_service.list(active_only=True)
|
167
|
+
|
168
|
+
if not access_credentials:
|
169
|
+
return
|
170
|
+
|
171
|
+
for credentials in access_credentials:
|
172
|
+
provider_service = get_provider_service_by_access_credentials(credentials)
|
173
|
+
if not provider_service:
|
174
|
+
self.notify(
|
175
|
+
f"Could not get storages list for {credentials.name}!",
|
176
|
+
severity="error",
|
177
|
+
)
|
178
|
+
continue
|
179
|
+
try:
|
180
|
+
storages = provider_service.list_storages()
|
181
|
+
self.storage_list_sidebar.storages = {
|
182
|
+
credentials.uuid: storages,
|
183
|
+
**self.storage_list_sidebar.storages,
|
184
|
+
}
|
185
|
+
except Exception:
|
186
|
+
self.notify(
|
187
|
+
f"Could not get storages list for {credentials.name}!",
|
188
|
+
severity="error",
|
189
|
+
)
|
190
|
+
|
191
|
+
@on(SelectStorageItem)
|
192
|
+
def on_select_storage_item(self, event: SelectStorageItem):
|
193
|
+
"""
|
194
|
+
Handles the selection of a storage item by updating the storage content
|
195
|
+
with the selected item's details and retrieving its contents.
|
196
|
+
|
197
|
+
Args:
|
198
|
+
event (SelectStorageItem): The event containing details of the selected storage item.
|
199
|
+
|
200
|
+
Flow:
|
201
|
+
1. Update storage_content with the path, storage name, and access credentials UUID from the event.
|
202
|
+
2. Retrieve the provider service using the access credentials UUID.
|
203
|
+
3. If the provider service is available, attempt to list storage items.
|
204
|
+
4. Update storage_content with the retrieved storage items.
|
205
|
+
5. Notify the user if an error occurs during the retrieval process.
|
206
|
+
"""
|
207
|
+
self.refresh_storage_content(
|
208
|
+
event.access_credentials_uuid, event.name, event.path, event.prefix
|
209
|
+
)
|
210
|
+
|
211
|
+
@on(UploadRequest)
|
212
|
+
def on_upload_request(self, event: UploadRequest):
|
213
|
+
"""
|
214
|
+
Handles file upload requests by opening a file system navigation modal.
|
215
|
+
|
216
|
+
This method is triggered when an UploadRequest event is received. It opens
|
217
|
+
a file system navigation modal to allow the user to select a file or directory
|
218
|
+
to upload, then calls the _upload_file method with the selected source path.
|
219
|
+
|
220
|
+
Args:
|
221
|
+
event (UploadRequest): The upload request event containing storage details
|
222
|
+
"""
|
223
|
+
self.push_screen(
|
224
|
+
FileSystemNavigationModal(),
|
225
|
+
callback=lambda src: self._upload_file(
|
226
|
+
event.access_credentials_uuid, event.storage, event.path, src # type: ignore
|
227
|
+
),
|
228
|
+
)
|
229
|
+
|
230
|
+
@on(DownloadRequest)
|
231
|
+
def on_download_request(self, event: DownloadRequest):
|
232
|
+
"""
|
233
|
+
Handles file download requests by opening a storage action progress screen.
|
234
|
+
|
235
|
+
This method is triggered when a DownloadRequest event is received. It creates
|
236
|
+
a StorageActionProgressScreen to track and display the progress of the download
|
237
|
+
operation for the selected files.
|
238
|
+
|
239
|
+
Args:
|
240
|
+
event (DownloadRequest): The download request event containing file details
|
241
|
+
"""
|
242
|
+
self.push_screen(
|
243
|
+
StorageActionProgressScreen(
|
244
|
+
storage_name=event.storage_name,
|
245
|
+
provider_service=get_provider_service_by_access_uuid(
|
246
|
+
event.access_credentials_uuid, self.credentials_service
|
247
|
+
),
|
248
|
+
path=event.path,
|
249
|
+
keys=[
|
250
|
+
DownloadKey(display_name=key, uuid=generate_uuid(), path=key) # type: ignore
|
251
|
+
for key in event.keys
|
252
|
+
],
|
253
|
+
action="download",
|
254
|
+
),
|
255
|
+
callback=lambda x: self.after_bulk_operation_callback(
|
256
|
+
event.access_credentials_uuid, event.storage_name, event.path
|
257
|
+
),
|
258
|
+
)
|
259
|
+
|
260
|
+
@on(DeleteRequest)
|
261
|
+
def on_delete_request(self, event: DeleteRequest):
|
262
|
+
"""
|
263
|
+
Handles file deletion requests by opening a storage action progress screen.
|
264
|
+
|
265
|
+
This method is triggered when a DeleteRequest event is received. It creates
|
266
|
+
a StorageActionProgressScreen to track and display the progress of the delete
|
267
|
+
operation for the selected files.
|
268
|
+
|
269
|
+
Args:
|
270
|
+
event (DeleteRequest): The delete request event containing file details
|
271
|
+
"""
|
272
|
+
self.push_screen(
|
273
|
+
StorageActionProgressScreen(
|
274
|
+
storage_name=event.storage_name,
|
275
|
+
provider_service=get_provider_service_by_access_uuid(
|
276
|
+
event.access_credentials_uuid, self.credentials_service
|
277
|
+
),
|
278
|
+
path=event.path,
|
279
|
+
keys=[
|
280
|
+
DeleteKey(display_name=key, uuid=generate_uuid(), path=key) for key in event.keys # type: ignore
|
281
|
+
],
|
282
|
+
action="delete",
|
283
|
+
),
|
284
|
+
callback=lambda x: self.after_bulk_operation_callback(
|
285
|
+
event.access_credentials_uuid, event.storage_name, event.path
|
286
|
+
),
|
287
|
+
)
|
288
|
+
|
289
|
+
def after_bulk_operation_callback(
|
290
|
+
self, access_credentials_uuid, storage_name, path
|
291
|
+
):
|
292
|
+
"""
|
293
|
+
Callback method executed after bulk operations (download, upload, delete) complete.
|
294
|
+
|
295
|
+
This method resets the storage content display and refreshes it with the latest
|
296
|
+
content from the specified storage path after a bulk operation completes.
|
297
|
+
|
298
|
+
Args:
|
299
|
+
access_credentials_uuid (str): UUID of the access credentials to use
|
300
|
+
storage_name (str): Name of the storage to display
|
301
|
+
path (str): Path within the storage to display
|
302
|
+
"""
|
303
|
+
self.reset_storage_content()
|
304
|
+
self.refresh_storage_content(access_credentials_uuid, storage_name, path)
|
305
|
+
|
306
|
+
@on(UncheckFilesRequest)
|
307
|
+
def uncheck_files_request(self, event: UncheckFilesRequest):
|
308
|
+
"""
|
309
|
+
Handles requests to uncheck selected files in the storage content view.
|
310
|
+
|
311
|
+
This method is triggered when an UncheckFilesRequest event is received.
|
312
|
+
It unchecks all files specified in the event and clears the selected files
|
313
|
+
tracking in the storage content widget.
|
314
|
+
|
315
|
+
Args:
|
316
|
+
event (UncheckFilesRequest): The uncheck request event containing file keys
|
317
|
+
"""
|
318
|
+
for key_uuid in event.keys:
|
319
|
+
file_item = self.query_one(f"#{key_uuid}")
|
320
|
+
file_item.uncheck() # type: ignore
|
321
|
+
self.storage_content.selected_files = set()
|
322
|
+
self.storage_content.selected_files_n = 0
|
323
|
+
|
324
|
+
@on(PreviewRequest)
|
325
|
+
def on_preview_request(self, event: PreviewRequest):
|
326
|
+
"""
|
327
|
+
Handles requests to preview file content.
|
328
|
+
|
329
|
+
This method is triggered when a PreviewRequest event is received. It attempts
|
330
|
+
to read the content of the specified file using the provider service and
|
331
|
+
displays it in a PreviewContentScreen if successful.
|
332
|
+
|
333
|
+
Args:
|
334
|
+
event (PreviewRequest): The preview request event containing file details
|
335
|
+
|
336
|
+
Note:
|
337
|
+
If an error occurs while reading the file content, a notification is shown
|
338
|
+
to the user and the preview is not displayed.
|
339
|
+
"""
|
340
|
+
self.push_screen(
|
341
|
+
PreviewContentScreen(
|
342
|
+
storage_name=event.storage_name,
|
343
|
+
key=event.path,
|
344
|
+
access_credentials_uuid=event.access_credentials_uuid,
|
345
|
+
)
|
346
|
+
)
|
347
|
+
|
348
|
+
def reset_storage_content(self):
|
349
|
+
"""
|
350
|
+
Resets the storage content attributes to their default state.
|
351
|
+
|
352
|
+
This method clears the current storage path, storage name, search prefix,
|
353
|
+
storage content, and access credentials UUID, effectively resetting the
|
354
|
+
storage content to an uninitialized state.
|
355
|
+
"""
|
356
|
+
self.storage_content.path = None
|
357
|
+
self.storage_content.storage = None
|
358
|
+
self.storage_content.search_prefix = None
|
359
|
+
self.storage_content.storage_content = None
|
360
|
+
self.storage_content.access_credentials_uuid = ""
|
361
|
+
self.storage_content.selected_files = set()
|
362
|
+
self.storage_content.selected_files_n = 0
|
363
|
+
self.uncheck_files_request(UncheckFilesRequest(keys=[]))
|
364
|
+
|
365
|
+
def refresh_storage_content(
|
366
|
+
self, access_credentials_uuid, storage_name, path, prefix=None
|
367
|
+
):
|
368
|
+
"""
|
369
|
+
Refreshes the storage content display with items from the specified storage path.
|
370
|
+
|
371
|
+
This method updates the storage content widget with items from the specified
|
372
|
+
storage path and provider. It handles retrieving the storage items using the
|
373
|
+
provider service and updating the UI accordingly.
|
374
|
+
|
375
|
+
Args:
|
376
|
+
access_credentials_uuid (str): UUID of the access credentials to use
|
377
|
+
storage_name (str): Name of the storage to display
|
378
|
+
path (str): Path within the storage to display
|
379
|
+
prefix (str, optional): Filter prefix for storage items. Defaults to None.
|
380
|
+
|
381
|
+
Note:
|
382
|
+
If an error occurs while retrieving storage items, a notification is shown
|
383
|
+
to the user and the storage content remains unchanged.
|
384
|
+
"""
|
385
|
+
self.storage_content.path = path.strip("/") if path else path
|
386
|
+
self.storage_content.storage = storage_name
|
387
|
+
self.storage_content.access_credentials_uuid = access_credentials_uuid
|
388
|
+
self.storage_content.search_prefix = prefix or ""
|
389
|
+
|
390
|
+
provider_service = get_provider_service_by_access_uuid(
|
391
|
+
access_credentials_uuid, self.credentials_service
|
392
|
+
)
|
393
|
+
|
394
|
+
if not provider_service:
|
395
|
+
self.notify("Could not extract storage content", severity="error")
|
396
|
+
return
|
397
|
+
params = {"storage": storage_name, "path": path or "", "prefix": prefix or ""}
|
398
|
+
try:
|
399
|
+
self.storage_content.storage_content = provider_service.list_storage_items(**params) # type: ignore
|
400
|
+
except ListStorageItemsException as e:
|
401
|
+
self.notify(
|
402
|
+
f"""Could not extract storage content
|
403
|
+
{str(e)}""",
|
404
|
+
severity="error",
|
405
|
+
)
|
406
|
+
|
407
|
+
def _upload_file(
|
408
|
+
self,
|
409
|
+
access_credentials_uuid: str,
|
410
|
+
storage_name: str,
|
411
|
+
path: str,
|
412
|
+
source_path: Path,
|
413
|
+
) -> None:
|
414
|
+
"""
|
415
|
+
Uploads a file to the specified storage.
|
416
|
+
|
417
|
+
This method handles the upload of a file to a cloud storage provider.
|
418
|
+
It creates an upload key, gets the provider service, and pushes a
|
419
|
+
progress screen to handle the upload operation.
|
420
|
+
|
421
|
+
Args:
|
422
|
+
access_credentials_uuid (str): The UUID of the access credentials used for authentication.
|
423
|
+
storage_name (str): The name of the storage where the file will be uploaded.
|
424
|
+
path (str): The destination path within the storage.
|
425
|
+
source_path (Path): The local path of the file to be uploaded.
|
426
|
+
file_system_service (FileSystemService, optional): Service for file system operations.
|
427
|
+
Defaults to Provide[DiContainer.file_system_service].
|
428
|
+
|
429
|
+
Returns:
|
430
|
+
None
|
431
|
+
|
432
|
+
Note:
|
433
|
+
If the source_path is None or the provider service is not available,
|
434
|
+
the method will return early without performing any upload.
|
435
|
+
"""
|
436
|
+
# Validate input parameters
|
437
|
+
if not source_path:
|
438
|
+
self.notify("No file selected for upload", severity="error")
|
439
|
+
return
|
440
|
+
|
441
|
+
# Get the provider service
|
442
|
+
provider_service = get_provider_service_by_access_uuid(
|
443
|
+
access_credentials_uuid, self.credentials_service
|
444
|
+
)
|
445
|
+
if not provider_service:
|
446
|
+
self.notify("Could not get provider service for upload", severity="error")
|
447
|
+
return
|
448
|
+
|
449
|
+
# Create upload key
|
450
|
+
upload_key = UploadKey(
|
451
|
+
display_name=source_path.name,
|
452
|
+
uuid=generate_uuid(),
|
453
|
+
path=source_path,
|
454
|
+
dest_path=str(source_path.name),
|
455
|
+
)
|
456
|
+
|
457
|
+
# Push the upload progress screen
|
458
|
+
self.push_screen(
|
459
|
+
StorageActionProgressScreen(
|
460
|
+
storage_name=storage_name,
|
461
|
+
provider_service=provider_service,
|
462
|
+
path=path,
|
463
|
+
keys=[upload_key],
|
464
|
+
action="upload",
|
465
|
+
),
|
466
|
+
callback=lambda x: self.after_bulk_operation_callback(
|
467
|
+
access_credentials_uuid, storage_name, path
|
468
|
+
),
|
469
|
+
)
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
|
3
|
+
from textual.message import Message
|
4
|
+
|
5
|
+
|
6
|
+
@dataclass
|
7
|
+
class ResizingRuleMove(Message):
|
8
|
+
orientation: str
|
9
|
+
delta: int
|
10
|
+
previous_component_id: str
|
11
|
+
next_component_id: str
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class ResizingRuleSelect(Message):
|
16
|
+
id: str
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class ResizingRuleRelease(Message):
|
21
|
+
id: str
|
File without changes
|