data-sourcerer 0.2.3__py3-none-any.whl → 0.4.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 (51) hide show
  1. {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/METADATA +3 -1
  2. {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/RECORD +50 -34
  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/domain/storage_provider/entities.py +1 -1
  10. sourcerer/infrastructure/access_credentials/repositories.py +3 -2
  11. sourcerer/infrastructure/access_credentials/services.py +9 -25
  12. sourcerer/infrastructure/db/models.py +33 -2
  13. sourcerer/infrastructure/storage/__init__.py +0 -0
  14. sourcerer/infrastructure/storage/repositories.py +72 -0
  15. sourcerer/infrastructure/storage/services.py +37 -0
  16. sourcerer/infrastructure/storage_provider/services/azure.py +1 -3
  17. sourcerer/infrastructure/storage_provider/services/gcp.py +2 -3
  18. sourcerer/infrastructure/storage_provider/services/s3.py +1 -2
  19. sourcerer/infrastructure/utils.py +2 -1
  20. sourcerer/presentation/di_container.py +15 -0
  21. sourcerer/presentation/screens/file_system_finder/main.py +5 -10
  22. sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +16 -13
  23. sourcerer/presentation/screens/main/main.py +89 -9
  24. sourcerer/presentation/screens/main/messages/preview_request.py +1 -0
  25. sourcerer/presentation/screens/main/messages/select_storage_item.py +1 -0
  26. sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +2 -1
  27. sourcerer/presentation/screens/main/widgets/storage_content.py +197 -80
  28. sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +99 -31
  29. sourcerer/presentation/screens/preview_content/main.py +216 -17
  30. sourcerer/presentation/screens/preview_content/styles.tcss +39 -4
  31. sourcerer/presentation/screens/preview_content/text_area_style.py +60 -0
  32. sourcerer/presentation/screens/provider_creds_list/main.py +38 -13
  33. sourcerer/presentation/screens/provider_creds_registration/main.py +10 -7
  34. sourcerer/presentation/screens/shared/modal_screens.py +37 -0
  35. sourcerer/presentation/screens/shared/widgets/spinner.py +57 -0
  36. sourcerer/presentation/screens/storage_action_progress/main.py +3 -5
  37. sourcerer/presentation/screens/storages_list/__init__.py +0 -0
  38. sourcerer/presentation/screens/storages_list/main.py +184 -0
  39. sourcerer/presentation/screens/storages_list/messages/__init__.py +0 -0
  40. sourcerer/presentation/screens/storages_list/messages/reload_storages_request.py +8 -0
  41. sourcerer/presentation/screens/storages_list/styles.tcss +55 -0
  42. sourcerer/presentation/screens/storages_registration/__init__.py +0 -0
  43. sourcerer/presentation/screens/storages_registration/main.py +100 -0
  44. sourcerer/presentation/screens/storages_registration/styles.tcss +41 -0
  45. sourcerer/presentation/settings.py +29 -16
  46. sourcerer/presentation/utils.py +9 -1
  47. sourcerer/settings.py +2 -0
  48. sourcerer/presentation/screens/shared/widgets/loader.py +0 -31
  49. {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/WHEEL +0 -0
  50. {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/entry_points.txt +0 -0
  51. {data_sourcerer-0.2.3.dist-info → data_sourcerer-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,57 @@
1
+ from collections.abc import Iterator
2
+ from enum import Enum
3
+ from itertools import cycle
4
+
5
+ from textual.app import RenderResult
6
+ from textual.widgets import Static
7
+
8
+
9
+ class SpinnerType(Enum):
10
+ """Enumeration of various spinner animations."""
11
+
12
+ dots_1 = "⣷⣯⣟⡿⢿⣻⣽⣾"
13
+ dots_2 = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
14
+ dots_3 = "⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓"
15
+ dots_4 = "⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆"
16
+ dots_5 = "⠈⠐⠠⢀⡀⠄⠂⠁"
17
+ dots_6 = "⋯⋱⋮⋰"
18
+ circles = "◐◓◑◒"
19
+ angles = "┐┤┘┴└├┌┬"
20
+ arrows = "←↖↑↗→↘↓↙"
21
+ moon = "🌑🌒🌓🌔🌕🌖🌗🌘"
22
+ clock = "🕛🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚"
23
+ histogram = "▁▃▄▅▆▇█▇▆▅▄▃"
24
+ shade = "░▒▓█▓▒░"
25
+ colors = "🟨🟨🟧🟧🟥🟥🟦🟦🟪🟪🟩🟩"
26
+ triangles = "◢◣◤◥"
27
+
28
+
29
+ class Spinner(Static):
30
+ """A loading spinner widget for Textual apps."""
31
+
32
+ DEFAULT_CSS = """
33
+ Spinner {
34
+ color: #9E53E0;
35
+ }
36
+ """
37
+
38
+ def __init__(
39
+ self, spinner: SpinnerType = SpinnerType.dots_1, interval: float = 0.1, **kwargs
40
+ ):
41
+ """
42
+ Initialize the Loader widget.
43
+
44
+ Args:
45
+ spinner (SpinnerType): The spinner animation type.
46
+ interval (float): Time in seconds between frames.
47
+ **kwargs: Additional keyword arguments for Static.
48
+ """
49
+ super().__init__(**kwargs)
50
+ self._frames: Iterator[str] = cycle(spinner.value)
51
+ self._interval = interval
52
+
53
+ def render(self) -> RenderResult:
54
+ return next(self._frames)
55
+
56
+ def on_mount(self) -> None:
57
+ self.auto_refresh = self._interval
@@ -298,7 +298,7 @@ class StorageActionProgressScreen(ModalScreen):
298
298
  progress_bar.total = file_size
299
299
  except Exception as ex:
300
300
  self.notify(
301
- f"Failed to get file size for {key}: {str(ex)}", severity="error"
301
+ f"Failed to get file size for {key}: {ex}", severity="error"
302
302
  )
303
303
  self.log.error(f"Error getting file size: {ex}")
304
304
  return
@@ -311,7 +311,7 @@ class StorageActionProgressScreen(ModalScreen):
311
311
  lambda chunk: progress_callback(progress_bar, chunk),
312
312
  )
313
313
  except Exception as ex:
314
- self.notify(f"Failed to download {key}: {str(ex)}", severity="error")
314
+ self.notify(f"Failed to download {key}: {ex}", severity="error")
315
315
  self.log.error(f"Error downloading file: {ex}")
316
316
  return
317
317
 
@@ -324,9 +324,7 @@ class StorageActionProgressScreen(ModalScreen):
324
324
  # Non-critical error, continue execution
325
325
  except Exception as ex:
326
326
  # Catch any unexpected exceptions
327
- self.notify(
328
- f"Unexpected error downloading {key}: {str(ex)}", severity="error"
329
- )
327
+ self.notify(f"Unexpected error downloading {key}: {ex}", severity="error")
330
328
  self.log.error(f"Unexpected error: {ex}")
331
329
  finally:
332
330
  main_progress_bar.advance(1)
@@ -0,0 +1,184 @@
1
+ import datetime
2
+ import uuid
3
+ from enum import Enum
4
+
5
+ from dependency_injector.wiring import Provide
6
+ from textual import on
7
+ from textual.app import ComposeResult
8
+ from textual.containers import Container, Horizontal, VerticalScroll
9
+ from textual.reactive import reactive
10
+ from textual.widgets import Label
11
+
12
+ from sourcerer.domain.storage.entities import Storage
13
+ from sourcerer.infrastructure.access_credentials.services import CredentialsService
14
+ from sourcerer.infrastructure.storage.services import StoragesService
15
+ from sourcerer.presentation.di_container import DiContainer
16
+ from sourcerer.presentation.screens.question.main import QuestionScreen
17
+ from sourcerer.presentation.screens.shared.modal_screens import (
18
+ RefreshTriggerableModalScreen,
19
+ )
20
+ from sourcerer.presentation.screens.shared.widgets.button import Button
21
+ from sourcerer.presentation.screens.storages_list.messages.reload_storages_request import (
22
+ ReloadStoragesRequest,
23
+ )
24
+ from sourcerer.presentation.screens.storages_registration.main import (
25
+ StorageEntry,
26
+ StoragesRegistrationScreen,
27
+ )
28
+
29
+
30
+ class ControlsEnum(Enum):
31
+ CANCEL = "Cancel"
32
+ ADD_STORAGE = "Add Storage"
33
+
34
+
35
+ class StorageRow(Horizontal):
36
+ def __init__(
37
+ self, storage: Storage, storages_service: StoragesService, *args, **kwargs
38
+ ):
39
+ super().__init__(*args, **kwargs)
40
+ self.storage = storage
41
+ self.storages_service = storages_service
42
+
43
+ def compose(self):
44
+ yield Label(self.storage.name, classes="storage_name")
45
+ yield Label(self.storage.credentials_name or "🚫", classes="credentials_name")
46
+ yield Button(
47
+ "❌",
48
+ name="delete_storage",
49
+ classes="storage_delete",
50
+ )
51
+
52
+ @on(Button.Click)
53
+ def on_button_click(self, _: Button.Click):
54
+ """
55
+ Handle delete button click events by showing a confirmation dialog for storage deletion.
56
+ Args:
57
+ _ (Button.Click): The button click event.
58
+ """
59
+ self.app.push_screen(
60
+ QuestionScreen(
61
+ f"Are you sure you want to delete {self.storage.credentials_name} {self.storage.name} storage?"
62
+ ),
63
+ callback=self.delete_callback, # type: ignore
64
+ )
65
+
66
+ def delete_callback(self, result: bool):
67
+ """
68
+ Callback function to handle the result of the confirmation screen.
69
+
70
+ Args:
71
+ result (bool): True if the user confirmed, False otherwise.
72
+ """
73
+ if not result:
74
+ return
75
+ self.storages_service.delete(self.storage.uuid)
76
+ self.post_message(ReloadStoragesRequest())
77
+
78
+
79
+ class StoragesListScreen(RefreshTriggerableModalScreen):
80
+ CSS_PATH = "styles.tcss"
81
+
82
+ MAIN_CONTAINER_ID = "StoragesListScreen"
83
+ SETTINGS_CONTAINER_ID = "settings"
84
+
85
+ storages_list = reactive([], recompose=True)
86
+
87
+ def __init__(
88
+ self,
89
+ credentials_service: CredentialsService = Provide[
90
+ DiContainer.credentials_service
91
+ ],
92
+ storages_service: StoragesService = Provide[DiContainer.storages_service],
93
+ *args,
94
+ **kwargs,
95
+ ):
96
+ super().__init__(*args, **kwargs)
97
+ self.storage_service = storages_service
98
+ self.credentials_service = credentials_service
99
+
100
+ def compose(self) -> ComposeResult:
101
+ with Container(id=self.MAIN_CONTAINER_ID):
102
+ yield Container(
103
+ Button(
104
+ "+Add new storage",
105
+ name=ControlsEnum.ADD_STORAGE.name,
106
+ classes="add_storage_button",
107
+ ),
108
+ id="right-top",
109
+ )
110
+ with VerticalScroll(id=self.SETTINGS_CONTAINER_ID):
111
+ with Horizontal():
112
+ yield Label("Storage Name", classes="storage_name")
113
+ yield Label("Credentials Name", classes="credentials_name")
114
+ yield Label("Delete", classes="storage_delete")
115
+ for storage in self.storages_list:
116
+ yield StorageRow(storage, self.storage_service)
117
+ with Horizontal(id="controls"):
118
+ yield Button(ControlsEnum.CANCEL.value, name=ControlsEnum.CANCEL.name)
119
+
120
+ def on_compose(self):
121
+ """
122
+ Initialize the screen by refreshing the credentials list when the screen is composed.
123
+ """
124
+ self.refresh_storages_list(set_refresh_flag=False)
125
+
126
+ def refresh_storages_list(self, set_refresh_flag: bool = True):
127
+ """
128
+ Refresh the storages list by retrieving the latest storages from the storage service.
129
+ """
130
+ self.storages_list = self.storage_service.list()
131
+ if set_refresh_flag:
132
+ self._requires_storage_refresh = True
133
+
134
+ @on(ReloadStoragesRequest)
135
+ def on_reload_storages_request(self, _: ReloadStoragesRequest):
136
+ """
137
+ Handle the reload storages request by refreshing the storages list.
138
+
139
+ Args:
140
+ _: ReloadStoragesRequest: The reload storages request message.
141
+ """
142
+ self.refresh_storages_list()
143
+
144
+ @on(Button.Click)
145
+ def on_control_button_click(self, event: Button.Click):
146
+ """
147
+ Handle click events for control buttons.
148
+
149
+ Dismisses the screen if the cancel button is clicked, or opens the provider credentials registration screen if
150
+ the add registration button is clicked.
151
+
152
+ Args:
153
+ event (Button.Click): The button click event.
154
+ """
155
+ if event.action == ControlsEnum.CANCEL.name:
156
+ self.action_cancel_screen()
157
+ if event.action == ControlsEnum.ADD_STORAGE.name:
158
+ self.app.push_screen(
159
+ StoragesRegistrationScreen(),
160
+ callback=self.create_storage_entry, # type: ignore
161
+ )
162
+
163
+ def create_storage_entry(self, storage: StorageEntry | None):
164
+ """
165
+ Create a new storage entry.
166
+
167
+ Creates a new storage entry using the provided data and refreshes the storage list.
168
+ """
169
+ if storage is None:
170
+ return
171
+
172
+ credentials = self.credentials_service.get(storage.credentials_uuid)
173
+ if not credentials:
174
+ self.notify("Credentials not found", severity="error")
175
+ return
176
+ self.storage_service.create(
177
+ Storage(
178
+ uuid=str(uuid.uuid4()),
179
+ name=storage.name,
180
+ credentials_id=credentials.id,
181
+ date_created=datetime.datetime.now(),
182
+ )
183
+ )
184
+ self.refresh_storages_list()
@@ -0,0 +1,8 @@
1
+ from dataclasses import dataclass
2
+
3
+ from textual.message import Message
4
+
5
+
6
+ @dataclass
7
+ class ReloadStoragesRequest(Message):
8
+ pass
@@ -0,0 +1,55 @@
1
+
2
+ Container {
3
+ height: auto;
4
+ }
5
+
6
+ Label {
7
+ padding-left: 1;
8
+ }
9
+
10
+ StoragesListScreen {
11
+ align: center middle;
12
+ content-align: center top;
13
+
14
+ & > #StoragesListScreen {
15
+ padding: 1 2 0 2;
16
+ margin: 0 0;
17
+ width: 60%;
18
+ height: 60%;
19
+ border: solid $secondary-background;
20
+
21
+ #right-top {
22
+ align: right top;
23
+
24
+ .add_storage_button {
25
+ color: $success-darken-2;
26
+ }
27
+
28
+ margin-bottom: 1;
29
+ }
30
+
31
+ ProviderCredentialsRow.active {
32
+ background: $primary-lighten-2;
33
+ }
34
+
35
+ ProviderCredentialsRow, Horizontal {
36
+ height: auto;
37
+ }
38
+
39
+ .storage_name {
40
+ width: 45%;
41
+ }
42
+
43
+ .credentials_name {
44
+ width: 35%;
45
+ }
46
+
47
+ .storage_delete {
48
+ width: 20%;
49
+ }
50
+
51
+ & > Select {
52
+ margin-bottom: 1;
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,100 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+
4
+ from dependency_injector.wiring import Provide
5
+ from textual import on
6
+ from textual.app import ComposeResult
7
+ from textual.containers import Container, Horizontal, VerticalScroll
8
+ from textual.widgets import Label, Select
9
+
10
+ from sourcerer.infrastructure.access_credentials.services import CredentialsService
11
+ from sourcerer.presentation.di_container import DiContainer
12
+ from sourcerer.presentation.screens.shared.modal_screens import ExitBoundModalScreen
13
+ from sourcerer.presentation.screens.shared.widgets.button import Button
14
+ from sourcerer.presentation.screens.shared.widgets.labeled_input import LabeledInput
15
+
16
+
17
+ class ControlsEnum(Enum):
18
+ CANCEL = "Cancel"
19
+ CREATE = "Create"
20
+
21
+
22
+ @dataclass
23
+ class StorageEntry:
24
+ name: str
25
+ credentials_uuid: str
26
+
27
+
28
+ class StoragesRegistrationScreen(ExitBoundModalScreen):
29
+ CSS_PATH = "styles.tcss"
30
+
31
+ MAIN_CONTAINER_ID = "StoragesRegistrationScreen"
32
+ SETTINGS_CONTAINER_ID = "settings"
33
+ CREDENTIALS_SELECTOR_ID = "credentials_selector"
34
+
35
+ PROVIDERS_NAME = "credentials"
36
+
37
+ def __init__(
38
+ self,
39
+ credentials_service: CredentialsService = Provide[
40
+ DiContainer.credentials_repository
41
+ ],
42
+ *args,
43
+ **kwargs,
44
+ ):
45
+ super().__init__(*args, **kwargs)
46
+ self.credentials = credentials_service.list()
47
+ self.auth_method = None
48
+
49
+ def compose(self) -> ComposeResult:
50
+ with Container(id=self.MAIN_CONTAINER_ID):
51
+ with VerticalScroll(id=self.SETTINGS_CONTAINER_ID):
52
+ yield LabeledInput(
53
+ "storage_name",
54
+ "Name:",
55
+ True,
56
+ multiline=False,
57
+ id="storage_name",
58
+ )
59
+ yield Label("* Credentials:", classes="form_label")
60
+ yield Select(
61
+ options=((item.name, item.uuid) for item in self.credentials),
62
+ name=self.PROVIDERS_NAME,
63
+ id=self.CREDENTIALS_SELECTOR_ID,
64
+ )
65
+ with Horizontal(id="controls"):
66
+ yield Button(ControlsEnum.CANCEL.value, name=ControlsEnum.CANCEL.name)
67
+ yield Button(ControlsEnum.CREATE.value, name=ControlsEnum.CREATE.name)
68
+
69
+ @on(Button.Click)
70
+ def on_control_button_click(self, event: Button.Click):
71
+ """
72
+ Handle click events for control buttons on the registration screen.
73
+
74
+ Depending on the action associated with the button click event, either dismiss
75
+ the screen or gather authentication fields and then dismiss the screen with
76
+ the collected data.
77
+
78
+ Args:
79
+ event (Button.Click): The click event containing the action to be performed.
80
+
81
+ Flow:
82
+ 1. Check if the event.action is ControlsEnum.cancel.name. If true, dismiss the screen.
83
+ 2. If event.action is ControlsEnum.create.name, gather authentication fields. Dismiss the screen with the
84
+ collected authentication fields.
85
+ """
86
+ if event.action == ControlsEnum.CANCEL.name:
87
+ self.action_cancel_screen()
88
+ elif event.action == ControlsEnum.CREATE.name:
89
+ storage_name = self.query_one("#storage_name", LabeledInput).get().value
90
+ if not storage_name:
91
+ self.notify("Storage name is required", severity="error")
92
+ return
93
+ credentials_uuid = self.query_one(
94
+ f"#{self.CREDENTIALS_SELECTOR_ID}", Select
95
+ ).value
96
+ if not credentials_uuid or credentials_uuid == Select.BLANK:
97
+ self.notify("Credentials are required", severity="error")
98
+ return
99
+
100
+ self.dismiss(StorageEntry(storage_name, credentials_uuid)) # type: ignore
@@ -0,0 +1,41 @@
1
+
2
+ Container {
3
+ height: auto;
4
+ }
5
+
6
+ Label {
7
+ padding-left: 1;
8
+ }
9
+
10
+ StoragesRegistrationScreen {
11
+ align: center middle;
12
+ content-align: center top;
13
+
14
+ & > #StoragesRegistrationScreen {
15
+ padding: 1 2 0 2;
16
+ margin: 0 0;
17
+ width: 70;
18
+ height: 17;
19
+ border: solid $secondary-background;
20
+
21
+ & > #settings {
22
+ width: 100%;
23
+ height: 11;
24
+
25
+ & > .form_label {
26
+ padding-left: 1;
27
+ margin-bottom: 1;
28
+
29
+ }
30
+
31
+ & > #credentials_fields_container {
32
+ margin-top: 2;
33
+ padding-left: 2;
34
+ }
35
+
36
+ & > Select {
37
+ margin-bottom: 1;
38
+ }
39
+ }
40
+ }
41
+ }
@@ -3,29 +3,42 @@
3
3
  This module contains various configuration constants and settings used throughout
4
4
  the application, including UI elements and display configurations.
5
5
  """
6
+ from enum import Enum
6
7
 
7
8
  NO_DATA_LOGO = """
8
- ### ###
9
- ### ###
10
- #### ####
11
- #### ####
12
- #######################
13
- ###### ######### ######
14
- ###### ######### ######
9
+ ### ###
10
+ ### ###
11
+ #### ####
12
+ #### ####
13
+ #######################
14
+ ###### ######### ######
15
+ ###### ######### ######
15
16
  ###################################
16
17
  ###################################
17
18
  ### ####################### ###
18
19
  ### ####################### ###
19
20
  ### ### ### ###
20
- ####### #######
21
- ####### #######
22
-
23
-
24
- ### ## ## #
25
- #### ## #### ###### #### #### #####
26
- ## ## ## ## ## ## ## #### # ####
27
- ## #### ## ## ## ## ## ## # ## ##
28
- ## ## #### ##### ###### ### ### #
21
+ ####### #######
22
+ ####### #######
23
+
24
+
25
+ ### ## ## #
26
+ #### ## #### ###### #### #### #####
27
+ ## ## ## ## ## ## ## #### # ####
28
+ ## #### ## ## ## ## ## ## # ## ##
29
+ ## ## #### ##### ###### ### ### #
29
30
  """
30
31
 
31
32
  MIN_SECTION_DIMENSION = 10
33
+
34
+
35
+ class KeyBindings(Enum):
36
+ """Enum representing key bindings for various actions in the application."""
37
+
38
+ ARROW_DOWN = "down"
39
+ ARROW_UP = "up"
40
+ ARROW_RIGHT = "right"
41
+ ARROW_LEFT = "left"
42
+ ENTER = "enter"
43
+ BACKSPACE = "backspace"
44
+ CTRL = "ctrl"
@@ -4,13 +4,16 @@ Utility functions for the presentation layer.
4
4
  This module provides helper functions for the presentation layer,
5
5
  particularly for retrieving and initializing storage provider services.
6
6
  """
7
+ from dependency_injector.wiring import Provide
7
8
 
9
+ from sourcerer.domain.access_credentials.repositories import BaseCredentialsRepository
8
10
  from sourcerer.domain.storage_provider.services import BaseStorageProviderService
9
11
  from sourcerer.infrastructure.access_credentials.exceptions import CredentialsAuthError
10
12
  from sourcerer.infrastructure.access_credentials.registry import (
11
13
  access_credential_method_registry,
12
14
  )
13
15
  from sourcerer.infrastructure.storage_provider.registry import storage_provider_registry
16
+ from sourcerer.presentation.di_container import DiContainer
14
17
 
15
18
 
16
19
  def get_provider_service_by_access_uuid(
@@ -32,6 +35,9 @@ def get_provider_service_by_access_uuid(
32
35
 
33
36
  def get_provider_service_by_access_credentials(
34
37
  credentials,
38
+ credentials_repo: BaseCredentialsRepository = Provide[
39
+ DiContainer.credentials_repository
40
+ ],
35
41
  ) -> BaseStorageProviderService | None:
36
42
  """
37
43
  Retrieves a storage provider service instance using the given access credentials.
@@ -67,7 +73,9 @@ def get_provider_service_by_access_credentials(
67
73
  return None
68
74
 
69
75
  try:
70
- auth_credentials = credentials_service().authenticate(credentials.credentials)
76
+ auth_credentials = credentials_service(credentials_repo).authenticate(
77
+ credentials.credentials
78
+ )
71
79
  except CredentialsAuthError:
72
80
  return None
73
81
  return provider_service_class(auth_credentials)
sourcerer/settings.py CHANGED
@@ -74,3 +74,5 @@ TEXT_EXTENSIONS = {
74
74
 
75
75
 
76
76
  PAGE_SIZE = 100
77
+ PREVIEW_LENGTH_LIMIT = 10_000
78
+ PREVIEW_LIMIT_SIZE = 2 * 1024 * 1024 # 2 MB
@@ -1,31 +0,0 @@
1
- from textual.app import ComposeResult
2
- from textual.containers import Container
3
- from textual.widgets import Static
4
-
5
-
6
- class Loader(Container):
7
- stops = "⣷⣯⣟⡿⢿⣻⣽⣾"
8
- index = 0
9
-
10
- DEFAULT_CSS = """
11
- Loader {
12
- color: #9E53E0;
13
- }
14
- """
15
-
16
- def __init__(self, *args, **kwargs):
17
- self.label = Static(self.stops[self.index])
18
- super().__init__(*args, **kwargs)
19
-
20
- def compose(self) -> ComposeResult:
21
- yield self.label
22
-
23
- def on_mount(self) -> None:
24
- self.auto_refresh = 1 / 10
25
-
26
- def automatic_refresh(self):
27
- self.index += 1
28
- if self.index >= len(self.stops):
29
- self.index = 0
30
- value = self.stops[self.index]
31
- self.label.update(value)