data-sourcerer 0.2.2__py3-none-any.whl → 0.3.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.
Files changed (44) hide show
  1. {data_sourcerer-0.2.2.dist-info → data_sourcerer-0.3.0.dist-info}/METADATA +1 -1
  2. {data_sourcerer-0.2.2.dist-info → data_sourcerer-0.3.0.dist-info}/RECORD +43 -29
  3. sourcerer/__init__.py +1 -1
  4. sourcerer/domain/access_credentials/entities.py +3 -1
  5. sourcerer/domain/access_credentials/repositories.py +1 -1
  6. sourcerer/domain/storage/__init__.py +0 -0
  7. sourcerer/domain/storage/entities.py +27 -0
  8. sourcerer/domain/storage/repositories.py +31 -0
  9. sourcerer/infrastructure/access_credentials/repositories.py +3 -2
  10. sourcerer/infrastructure/access_credentials/services.py +9 -25
  11. sourcerer/infrastructure/db/models.py +33 -2
  12. sourcerer/infrastructure/storage/__init__.py +0 -0
  13. sourcerer/infrastructure/storage/repositories.py +72 -0
  14. sourcerer/infrastructure/storage/services.py +37 -0
  15. sourcerer/infrastructure/storage_provider/services/gcp.py +1 -1
  16. sourcerer/infrastructure/utils.py +2 -1
  17. sourcerer/presentation/di_container.py +15 -0
  18. sourcerer/presentation/screens/file_system_finder/main.py +7 -6
  19. sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +16 -13
  20. sourcerer/presentation/screens/main/main.py +63 -8
  21. sourcerer/presentation/screens/main/messages/select_storage_item.py +1 -0
  22. sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +2 -1
  23. sourcerer/presentation/screens/main/widgets/storage_content.py +191 -85
  24. sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +99 -31
  25. sourcerer/presentation/screens/preview_content/main.py +15 -3
  26. sourcerer/presentation/screens/provider_creds_list/main.py +29 -8
  27. sourcerer/presentation/screens/provider_creds_registration/main.py +7 -4
  28. sourcerer/presentation/screens/shared/widgets/labeled_input.py +2 -2
  29. sourcerer/presentation/screens/shared/widgets/spinner.py +57 -0
  30. sourcerer/presentation/screens/storage_action_progress/main.py +7 -11
  31. sourcerer/presentation/screens/storages_list/__init__.py +0 -0
  32. sourcerer/presentation/screens/storages_list/main.py +180 -0
  33. sourcerer/presentation/screens/storages_list/messages/__init__.py +0 -0
  34. sourcerer/presentation/screens/storages_list/messages/reload_storages_request.py +8 -0
  35. sourcerer/presentation/screens/storages_list/styles.tcss +55 -0
  36. sourcerer/presentation/screens/storages_registration/__init__.py +0 -0
  37. sourcerer/presentation/screens/storages_registration/main.py +100 -0
  38. sourcerer/presentation/screens/storages_registration/styles.tcss +41 -0
  39. sourcerer/presentation/settings.py +29 -16
  40. sourcerer/presentation/utils.py +9 -1
  41. sourcerer/presentation/screens/shared/widgets/loader.py +0 -31
  42. {data_sourcerer-0.2.2.dist-info → data_sourcerer-0.3.0.dist-info}/WHEEL +0 -0
  43. {data_sourcerer-0.2.2.dist-info → data_sourcerer-0.3.0.dist-info}/entry_points.txt +0 -0
  44. {data_sourcerer-0.2.2.dist-info → data_sourcerer-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -21,6 +21,7 @@ from sourcerer.presentation.screens.shared.containers import (
21
21
  ScrollHorizontalContainerWithNoBindings,
22
22
  ScrollVerticalContainerWithNoBindings,
23
23
  )
24
+ from sourcerer.presentation.settings import KeyBindings
24
25
  from sourcerer.settings import DIRECTORY_ICON, DOUBLE_CLICK_THRESHOLD, FILE_ICON
25
26
 
26
27
 
@@ -48,7 +49,7 @@ class FileSystemWidget(Widget):
48
49
  background: $block-cursor-blurred-background;
49
50
  text-style: $block-cursor-blurred-text-style;
50
51
  }
51
-
52
+
52
53
  .folder-name {
53
54
  text-overflow: ellipsis;
54
55
  text-wrap: nowrap;
@@ -161,7 +162,7 @@ class FileSystemWidget(Widget):
161
162
  Calls the `on_click` method if the pressed key is "enter", which may trigger
162
163
  folder navigation or file opening depending on the widget's context.
163
164
  """
164
- if event.key == "enter":
165
+ if event.key == KeyBindings.ENTER.value:
165
166
  self.on_file_select()
166
167
 
167
168
  def on_file_select(self):
@@ -232,11 +233,13 @@ class FileSystemNavigator(ScrollHorizontalContainerWithNoBindings):
232
233
 
233
234
  # Consolidate key binding data
234
235
  BINDINGS: ClassVar[list[BindingType]] = [
235
- Binding("enter", "select_cursor", "Select", show=False),
236
- Binding("up", "cursor_up", "Cursor up", show=False),
237
- Binding("down", "cursor_down", "Cursor down", show=False),
238
- Binding("left", "cursor_left", "Cursor left", show=False),
239
- Binding("right", "cursor_right", "Cursor right", show=False),
236
+ Binding(KeyBindings.ENTER.value, "select_cursor", "Select", show=False),
237
+ Binding(KeyBindings.ARROW_UP.value, "cursor_up", "Cursor up", show=False),
238
+ Binding(KeyBindings.ARROW_DOWN.value, "cursor_down", "Cursor down", show=False),
239
+ Binding(KeyBindings.ARROW_LEFT.value, "cursor_left", "Cursor left", show=False),
240
+ Binding(
241
+ KeyBindings.ARROW_RIGHT.value, "cursor_right", "Cursor right", show=False
242
+ ),
240
243
  ]
241
244
 
242
245
  MAIN_CONTAINER_ID: ClassVar[str] = "dirs_content"
@@ -306,9 +309,9 @@ class FileSystemNavigator(ScrollHorizontalContainerWithNoBindings):
306
309
  )
307
310
  self._focus_first_child(path_listing_container)
308
311
 
309
- self.path_listing_containers_uuids[str(self.work_dir)] = (
310
- path_listing_container.id
311
- )
312
+ self.path_listing_containers_uuids[
313
+ str(self.work_dir)
314
+ ] = path_listing_container.id
312
315
 
313
316
  def action_cursor_down(self) -> None:
314
317
  """
@@ -626,9 +629,9 @@ class FileSystemNavigator(ScrollHorizontalContainerWithNoBindings):
626
629
  await self._mount_path_listing_container(path_listing_container)
627
630
 
628
631
  if str(folder_path) not in self.path_listing_containers_uuids:
629
- self.path_listing_containers_uuids[str(folder_path)] = (
630
- path_listing_container.id
631
- )
632
+ self.path_listing_containers_uuids[
633
+ str(folder_path)
634
+ ] = path_listing_container.id
632
635
 
633
636
  @on(FileSystemWidget.Focus)
634
637
  def on_folder_focus(self, event: FileSystemWidget.Focus):
@@ -2,19 +2,23 @@ import time
2
2
  import traceback
3
3
  from concurrent.futures import ThreadPoolExecutor
4
4
  from pathlib import Path
5
+ from typing import ClassVar
5
6
 
7
+ from dependency_injector.wiring import Provide
6
8
  from textual import on, work
7
9
  from textual.app import App, ComposeResult
8
- from textual.binding import Binding
10
+ from textual.binding import Binding, BindingType
9
11
  from textual.containers import Horizontal
10
12
  from textual.reactive import reactive
11
13
  from textual.widgets import Footer
12
14
 
15
+ from sourcerer.domain.storage_provider.entities import Storage
13
16
  from sourcerer.infrastructure.access_credentials.services import CredentialsService
14
17
  from sourcerer.infrastructure.storage_provider.exceptions import (
15
18
  ListStorageItemsError,
16
19
  )
17
20
  from sourcerer.infrastructure.utils import generate_uuid
21
+ from sourcerer.presentation.di_container import DiContainer
18
22
  from sourcerer.presentation.screens.critical_error.main import CriticalErrorScreen
19
23
  from sourcerer.presentation.screens.file_system_finder.main import (
20
24
  FileSystemNavigationModal,
@@ -55,6 +59,8 @@ from sourcerer.presentation.screens.storage_action_progress.main import (
55
59
  StorageActionProgressScreen,
56
60
  UploadKey,
57
61
  )
62
+ from sourcerer.presentation.screens.storages_list.main import StoragesListScreen
63
+ from sourcerer.presentation.settings import KeyBindings
58
64
  from sourcerer.presentation.themes.github_dark import github_dark_theme
59
65
  from sourcerer.presentation.utils import (
60
66
  get_provider_service_by_access_credentials,
@@ -93,12 +99,28 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
93
99
  """
94
100
 
95
101
  CSS_PATH = "styles.tcss"
96
- BINDINGS = [Binding("ctrl+r", "registrations", "Registrations list")]
102
+ BINDINGS: ClassVar[list[BindingType]] = [
103
+ Binding("ctrl+r", "registrations", "Registrations list"),
104
+ Binding("ctrl+s", "storages", "Storages list"),
105
+ Binding(
106
+ KeyBindings.ARROW_LEFT.value, "focus_sidebar", "Focus sidebar", show=False
107
+ ),
108
+ Binding(
109
+ KeyBindings.ARROW_RIGHT.value, "focus_content", "Focus content", show=False
110
+ ),
111
+ ]
97
112
  is_storage_list_loading = reactive(False, recompose=True)
98
113
 
99
- def __init__(self, *args, **kwargs):
114
+ def __init__(
115
+ self,
116
+ credentials_service: CredentialsService = Provide[
117
+ DiContainer.credentials_service
118
+ ],
119
+ *args,
120
+ **kwargs,
121
+ ):
100
122
  super().__init__(*args, **kwargs)
101
- self.credentials_service = CredentialsService()
123
+ self.credentials_service = credentials_service
102
124
  self.storage_list_sidebar = StorageListSidebar(id="storage_list_sidebar")
103
125
  self.storage_content = StorageContentContainer(id="storage_content_container")
104
126
  self.load_percentage = 0
@@ -130,6 +152,18 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
130
152
  self.theme = "github-dark"
131
153
  self.init_storages_list()
132
154
 
155
+ def action_focus_content(self):
156
+ """
157
+ Focuses the storage content container.
158
+ """
159
+ self.storage_content.focus()
160
+
161
+ def action_focus_sidebar(self):
162
+ """
163
+ Focuses the storage list sidebar.
164
+ """
165
+ self.storage_list_sidebar.focus()
166
+
133
167
  def action_registrations(self):
134
168
  """
135
169
  Opens the provider credentials list screen and refreshes the storage list.
@@ -142,6 +176,9 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
142
176
  """
143
177
  self.app.push_screen(ProviderCredsListScreen(), callback=self.refresh_storages)
144
178
 
179
+ def action_storages(self):
180
+ self.app.push_screen(StoragesListScreen(), callback=self.refresh_storages)
181
+
145
182
  def refresh_storages(self, *args, **kwargs):
146
183
  """
147
184
  Refreshes the storage list by clearing the current storages and
@@ -206,7 +243,11 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
206
243
  5. Notify the user if an error occurs during the retrieval process.
207
244
  """
208
245
  self.refresh_storage_content(
209
- event.access_credentials_uuid, event.name, event.path, event.prefix
246
+ event.access_credentials_uuid,
247
+ event.name,
248
+ event.path,
249
+ event.prefix,
250
+ event.focus_content,
210
251
  )
211
252
 
212
253
  @on(UploadRequest)
@@ -381,7 +422,12 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
381
422
  self.uncheck_files_request(UncheckFilesRequest(keys=[]))
382
423
 
383
424
  def refresh_storage_content(
384
- self, access_credentials_uuid, storage_name, path, prefix=None
425
+ self,
426
+ access_credentials_uuid,
427
+ storage_name,
428
+ path,
429
+ prefix=None,
430
+ focus_content=False,
385
431
  ):
386
432
  """
387
433
  Refreshes the storage content display with items from the specified storage path.
@@ -407,6 +453,7 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
407
453
  self.storage_content.storage_content = None
408
454
  self.storage_content.selected_files = set()
409
455
  self.storage_content.selected_files_n = 0
456
+ self.storage_content.focus_content = focus_content
410
457
 
411
458
  provider_service = get_provider_service_by_access_uuid(
412
459
  access_credentials_uuid, self.credentials_service
@@ -421,7 +468,7 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
421
468
  **params
422
469
  )
423
470
  except ListStorageItemsError as e:
424
- self.notify_error(f"""Could not extract storage content \n{str(e)}""")
471
+ self.notify_error(f"""Could not extract storage content \n{e}""")
425
472
 
426
473
  def _upload_file(
427
474
  self,
@@ -509,7 +556,15 @@ class Sourcerer(App, ResizeContainersWatcherMixin):
509
556
 
510
557
  try:
511
558
  storages = provider_service.list_storages()
512
- self.storage_list_sidebar.storages[credentials.uuid] = storages
559
+ storage_names = [storage.storage for storage in storages]
560
+ registered_storages = [
561
+ Storage(credentials.provider, storage.name, storage.created_at)
562
+ for storage in credentials.storages
563
+ if storage.name not in storage_names
564
+ ]
565
+ self.storage_list_sidebar.storages[credentials.uuid] = (
566
+ storages + registered_storages
567
+ )
513
568
  self.storage_list_sidebar.last_update_timestamp = time.time()
514
569
  except Exception:
515
570
  self.notify_error(f"Could not get storages list for {credentials.name}!")
@@ -9,3 +9,4 @@ class SelectStorageItem(Message):
9
9
  path: str | None = None
10
10
  access_credentials_uuid: str | None = None
11
11
  prefix: str | None = None
12
+ focus_content: bool = False
@@ -1,4 +1,5 @@
1
1
  import time
2
+ from typing import ClassVar
2
3
 
3
4
  from textual.events import MouseMove, MouseUp
4
5
 
@@ -28,7 +29,7 @@ class ResizeContainersWatcherMixin:
28
29
  and is inherited from textual App.
29
30
  """
30
31
 
31
- required_methods = ["query_one", "refresh"]
32
+ required_methods: ClassVar[list[str]] = ["query_one", "refresh"]
32
33
 
33
34
  def __init_subclass__(cls, **kwargs):
34
35
  super().__init_subclass__(**kwargs)