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.
Files changed (95) hide show
  1. data_sourcerer-0.1.0.dist-info/METADATA +52 -0
  2. data_sourcerer-0.1.0.dist-info/RECORD +95 -0
  3. data_sourcerer-0.1.0.dist-info/WHEEL +5 -0
  4. data_sourcerer-0.1.0.dist-info/entry_points.txt +2 -0
  5. data_sourcerer-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. data_sourcerer-0.1.0.dist-info/top_level.txt +1 -0
  7. sourcerer/__init__.py +15 -0
  8. sourcerer/domain/__init__.py +13 -0
  9. sourcerer/domain/access_credentials/__init__.py +7 -0
  10. sourcerer/domain/access_credentials/entities.py +53 -0
  11. sourcerer/domain/access_credentials/exceptions.py +17 -0
  12. sourcerer/domain/access_credentials/repositories.py +84 -0
  13. sourcerer/domain/access_credentials/services.py +91 -0
  14. sourcerer/domain/file_system/__init__.py +7 -0
  15. sourcerer/domain/file_system/entities.py +70 -0
  16. sourcerer/domain/file_system/exceptions.py +17 -0
  17. sourcerer/domain/file_system/services.py +64 -0
  18. sourcerer/domain/shared/__init__.py +6 -0
  19. sourcerer/domain/shared/entities.py +18 -0
  20. sourcerer/domain/storage_provider/__init__.py +7 -0
  21. sourcerer/domain/storage_provider/entities.py +86 -0
  22. sourcerer/domain/storage_provider/exceptions.py +17 -0
  23. sourcerer/domain/storage_provider/services.py +130 -0
  24. sourcerer/infrastructure/__init__.py +13 -0
  25. sourcerer/infrastructure/access_credentials/__init__.py +7 -0
  26. sourcerer/infrastructure/access_credentials/exceptions.py +16 -0
  27. sourcerer/infrastructure/access_credentials/registry.py +120 -0
  28. sourcerer/infrastructure/access_credentials/repositories.py +119 -0
  29. sourcerer/infrastructure/access_credentials/services.py +396 -0
  30. sourcerer/infrastructure/db/__init__.py +6 -0
  31. sourcerer/infrastructure/db/config.py +73 -0
  32. sourcerer/infrastructure/db/models.py +47 -0
  33. sourcerer/infrastructure/file_system/__init__.py +7 -0
  34. sourcerer/infrastructure/file_system/exceptions.py +89 -0
  35. sourcerer/infrastructure/file_system/services.py +147 -0
  36. sourcerer/infrastructure/storage_provider/__init__.py +7 -0
  37. sourcerer/infrastructure/storage_provider/exceptions.py +78 -0
  38. sourcerer/infrastructure/storage_provider/registry.py +84 -0
  39. sourcerer/infrastructure/storage_provider/services.py +509 -0
  40. sourcerer/infrastructure/utils.py +106 -0
  41. sourcerer/presentation/__init__.py +12 -0
  42. sourcerer/presentation/app.py +36 -0
  43. sourcerer/presentation/di_container.py +46 -0
  44. sourcerer/presentation/screens/__init__.py +0 -0
  45. sourcerer/presentation/screens/critical_error/__init__.py +0 -0
  46. sourcerer/presentation/screens/critical_error/main.py +78 -0
  47. sourcerer/presentation/screens/critical_error/styles.tcss +41 -0
  48. sourcerer/presentation/screens/file_system_finder/main.py +248 -0
  49. sourcerer/presentation/screens/file_system_finder/styles.tcss +44 -0
  50. sourcerer/presentation/screens/file_system_finder/widgets/__init__.py +0 -0
  51. sourcerer/presentation/screens/file_system_finder/widgets/file_system_navigator.py +810 -0
  52. sourcerer/presentation/screens/main/__init__.py +0 -0
  53. sourcerer/presentation/screens/main/main.py +469 -0
  54. sourcerer/presentation/screens/main/messages/__init__.py +0 -0
  55. sourcerer/presentation/screens/main/messages/delete_request.py +12 -0
  56. sourcerer/presentation/screens/main/messages/download_request.py +12 -0
  57. sourcerer/presentation/screens/main/messages/preview_request.py +10 -0
  58. sourcerer/presentation/screens/main/messages/resizing_rule.py +21 -0
  59. sourcerer/presentation/screens/main/messages/select_storage_item.py +11 -0
  60. sourcerer/presentation/screens/main/messages/uncheck_files_request.py +8 -0
  61. sourcerer/presentation/screens/main/messages/upload_request.py +10 -0
  62. sourcerer/presentation/screens/main/mixins/__init__.py +0 -0
  63. sourcerer/presentation/screens/main/mixins/resize_containers_watcher_mixin.py +144 -0
  64. sourcerer/presentation/screens/main/styles.tcss +32 -0
  65. sourcerer/presentation/screens/main/widgets/__init__.py +0 -0
  66. sourcerer/presentation/screens/main/widgets/gradient.py +45 -0
  67. sourcerer/presentation/screens/main/widgets/resizing_rule.py +67 -0
  68. sourcerer/presentation/screens/main/widgets/storage_content.py +691 -0
  69. sourcerer/presentation/screens/main/widgets/storage_list_sidebar.py +143 -0
  70. sourcerer/presentation/screens/preview_content/__init__.py +0 -0
  71. sourcerer/presentation/screens/preview_content/main.py +59 -0
  72. sourcerer/presentation/screens/preview_content/styles.tcss +26 -0
  73. sourcerer/presentation/screens/provider_creds_list/__init__.py +0 -0
  74. sourcerer/presentation/screens/provider_creds_list/main.py +164 -0
  75. sourcerer/presentation/screens/provider_creds_list/styles.tcss +65 -0
  76. sourcerer/presentation/screens/provider_creds_registration/__init__.py +0 -0
  77. sourcerer/presentation/screens/provider_creds_registration/main.py +264 -0
  78. sourcerer/presentation/screens/provider_creds_registration/styles.tcss +42 -0
  79. sourcerer/presentation/screens/question/__init__.py +0 -0
  80. sourcerer/presentation/screens/question/main.py +31 -0
  81. sourcerer/presentation/screens/question/styles.tcss +33 -0
  82. sourcerer/presentation/screens/shared/__init__.py +0 -0
  83. sourcerer/presentation/screens/shared/containers.py +13 -0
  84. sourcerer/presentation/screens/shared/widgets/__init__.py +0 -0
  85. sourcerer/presentation/screens/shared/widgets/button.py +54 -0
  86. sourcerer/presentation/screens/shared/widgets/labeled_input.py +80 -0
  87. sourcerer/presentation/screens/storage_action_progress/__init__.py +0 -0
  88. sourcerer/presentation/screens/storage_action_progress/main.py +476 -0
  89. sourcerer/presentation/screens/storage_action_progress/styles.tcss +43 -0
  90. sourcerer/presentation/settings.py +31 -0
  91. sourcerer/presentation/themes/__init__.py +0 -0
  92. sourcerer/presentation/themes/github_dark.py +21 -0
  93. sourcerer/presentation/utils.py +69 -0
  94. sourcerer/settings.py +72 -0
  95. sourcerer/utils.py +32 -0
@@ -0,0 +1,143 @@
1
+ """Storage list sidebar widget for the Sourcerer application.
2
+
3
+ This module provides widgets for displaying and interacting with the list of
4
+ storage providers in the sidebar. It handles storage grouping by provider type
5
+ and selection of storage items.
6
+ """
7
+
8
+ from collections import namedtuple
9
+ from itertools import groupby
10
+ from typing import Dict, List
11
+
12
+ from textual import events
13
+ from textual.app import ComposeResult
14
+ from textual.containers import VerticalScroll, Horizontal, Container
15
+ from textual.reactive import reactive
16
+ from textual.widgets import Label, Rule
17
+
18
+ from sourcerer.domain.shared.entities import StorageProvider
19
+ from sourcerer.domain.storage_provider.entities import Storage
20
+ from sourcerer.presentation.screens.main.messages.select_storage_item import (
21
+ SelectStorageItem,
22
+ )
23
+ from sourcerer.presentation.screens.main.widgets.gradient import GradientWidget
24
+
25
+ STORAGE_ICONS = {
26
+ StorageProvider.S3: "🟠",
27
+ StorageProvider.GoogleCloudStorage: "🔵",
28
+ "azure": "⚪️",
29
+ }
30
+ """Mapping of storage provider types to their display icons."""
31
+
32
+
33
+ class StorageItem(Label):
34
+ """Widget for displaying and interacting with a single storage item.
35
+
36
+ This widget represents a storage instance in the sidebar list, allowing
37
+ selection and visual feedback on hover.
38
+ """
39
+
40
+ DEFAULT_CSS = """
41
+ StorageItem {
42
+ width: 90%;
43
+ padding-left: 1;
44
+ height: auto;
45
+ margin:0;
46
+ text-overflow: ellipsis;
47
+ text-wrap: nowrap;
48
+
49
+ & > :hover {
50
+ background: $warning;
51
+ color: $panel;
52
+ }
53
+ }
54
+ """
55
+
56
+ def __init__(self, storage_name, access_credentials_uuid, *args, **kwargs):
57
+ """Initialize a storage item widget.
58
+
59
+ Args:
60
+ storage_name: The name of the storage instance
61
+ access_credentials_uuid: UUID of the access credentials being used
62
+ """
63
+ self.storage_name = storage_name
64
+ self.access_credentials_uuid = access_credentials_uuid
65
+
66
+ super().__init__(*args, **kwargs)
67
+
68
+ def on_click(self, _: events.Click) -> None:
69
+ """Handle click events to select the storage item."""
70
+ self.post_message(
71
+ SelectStorageItem(
72
+ self.storage_name, access_credentials_uuid=self.access_credentials_uuid
73
+ )
74
+ )
75
+
76
+
77
+ class StorageListSidebar(VerticalScroll):
78
+ """Sidebar widget for displaying the list of storage providers.
79
+
80
+ This widget manages the display of storage providers grouped by their type,
81
+ showing provider icons and storage names in a scrollable list.
82
+
83
+ Attributes:
84
+ storages: Dictionary mapping provider types to lists of storage instances
85
+ """
86
+
87
+ storages: reactive[Dict[str, List[Storage]]] = reactive({}, recompose=True)
88
+
89
+ DEFAULT_CSS = """
90
+ StorageListSidebar {
91
+ padding-right: 0;
92
+ margin-right: 0;
93
+ & > .storage-group {
94
+ height: auto;
95
+ margin-bottom: 1;
96
+ #rule-left {
97
+ width: 1;
98
+ }
99
+
100
+ Horizontal {
101
+ height: auto;
102
+ }
103
+ Rule.-horizontal {
104
+ height: 1;
105
+ margin: 0 0;
106
+
107
+ }
108
+ .storage-letter {
109
+ color: $secondary;
110
+ padding: 0 1;
111
+ }
112
+
113
+ }
114
+ }
115
+ """
116
+
117
+ def compose(self) -> ComposeResult:
118
+ with Container(id="header"):
119
+ yield GradientWidget("🧙SOURCERER", id="left-middle")
120
+ StorageData = namedtuple("Storage", ["access_credentials_uuid", "storage"])
121
+ storages = [
122
+ StorageData(access_credentials_uuid, storage)
123
+ for access_credentials_uuid, storages in self.storages.items()
124
+ for storage in storages
125
+ ]
126
+ storages = sorted(storages, key=lambda x: x.storage.storage)
127
+
128
+ for letter, storages in groupby(storages, key=lambda x: x.storage.storage[0]):
129
+ with Container(id=f"group-{letter}", classes="storage-group"):
130
+ yield Horizontal(
131
+ Rule(id="rule-left"),
132
+ Label(letter.upper(), classes="storage-letter"),
133
+ Rule(),
134
+ )
135
+
136
+ for item in storages:
137
+ yield StorageItem(
138
+ renderable=STORAGE_ICONS.get(item.storage.provider, "")
139
+ + " "
140
+ + item.storage.storage,
141
+ storage_name=item.storage.storage,
142
+ access_credentials_uuid=item.access_credentials_uuid,
143
+ )
@@ -0,0 +1,59 @@
1
+ from rich.syntax import Syntax
2
+ from textual import on
3
+ from textual.app import ComposeResult
4
+ from textual.containers import Container, Horizontal
5
+ from textual.screen import ModalScreen
6
+ from textual.widgets import RichLog, LoadingIndicator
7
+
8
+ from sourcerer.infrastructure.access_credentials.services import CredentialsService
9
+ from sourcerer.infrastructure.storage_provider.exceptions import (
10
+ ReadStorageItemsException,
11
+ )
12
+ from sourcerer.presentation.screens.shared.widgets.button import Button
13
+ from sourcerer.presentation.utils import get_provider_service_by_access_uuid
14
+
15
+
16
+ class PreviewContentScreen(ModalScreen):
17
+ CSS_PATH = "styles.tcss"
18
+
19
+ def __init__(self, storage_name, key, access_credentials_uuid, *args, **kwargs):
20
+ super().__init__(*args, **kwargs)
21
+
22
+ self.storage_name = storage_name
23
+ self.key = key
24
+ self.access_credentials_uuid = access_credentials_uuid
25
+
26
+ def compose(self) -> ComposeResult:
27
+ with Container(id="PreviewContentScreen"):
28
+ yield LoadingIndicator(id="loading")
29
+ yield RichLog(highlight=True, markup=True, auto_scroll=False)
30
+ with Horizontal(id="controls"):
31
+ yield Button("Close", name="cancel")
32
+
33
+ def on_mount(self) -> None:
34
+ """Called when the DOM is ready."""
35
+
36
+ credentials_service = CredentialsService()
37
+ provider_service = get_provider_service_by_access_uuid(
38
+ self.access_credentials_uuid, credentials_service
39
+ )
40
+ if not provider_service:
41
+ self.notify("Could not read file :(", severity="error")
42
+ return
43
+ try:
44
+ content = provider_service.read_storage_item(self.storage_name, self.key)
45
+ except ReadStorageItemsException:
46
+ self.notify("Could not read file :(", severity="error")
47
+ return
48
+ self.query_one("#loading").remove()
49
+
50
+ text_log = self.query_one(RichLog)
51
+ lexer = Syntax.guess_lexer(self.key, content)
52
+ content = Syntax(content, lexer, line_numbers=True, theme="ansi_dark")
53
+ text_log.write(content)
54
+
55
+ @on(Button.Click)
56
+ def on_button_click(self, event: Button.Click) -> None:
57
+ """Handle button click events."""
58
+ if event.action == "cancel":
59
+ self.dismiss()
@@ -0,0 +1,26 @@
1
+
2
+ Container {
3
+ height: auto;
4
+ }
5
+
6
+
7
+ PreviewContentScreen {
8
+ align: center middle;
9
+ content-align: center top;
10
+
11
+
12
+ & > #PreviewContentScreen {
13
+ padding: 1 2 0 2;
14
+ margin: 0 0;
15
+ width: 90;
16
+ height: 40;
17
+ border: solid $secondary-background;
18
+ border-title-color: $primary-lighten-2;
19
+
20
+ RichLog {
21
+ background: transparent;
22
+ }
23
+
24
+ }
25
+ }
26
+
@@ -0,0 +1,164 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+
4
+ from textual import on
5
+ from textual.app import ComposeResult
6
+ from textual.containers import Container, Horizontal, VerticalScroll
7
+ from textual.message import Message
8
+ from textual.reactive import reactive
9
+ from textual.screen import ModalScreen
10
+ from textual.widgets import Label, Checkbox
11
+
12
+ from sourcerer.domain.access_credentials.entities import Credentials
13
+ from sourcerer.infrastructure.access_credentials.services import CredentialsService
14
+ from sourcerer.presentation.screens.provider_creds_registration.main import (
15
+ ProviderCredsRegistrationScreen,
16
+ ProviderCredentialsEntry,
17
+ )
18
+ from sourcerer.presentation.screens.shared.widgets.button import Button
19
+
20
+
21
+ class ControlsEnum(Enum):
22
+ CANCEL = "Cancel"
23
+
24
+
25
+ class ProviderCredentialsRow(Horizontal):
26
+ @dataclass
27
+ class ChangeActiveStatus(Message):
28
+ uuid: str
29
+ active: bool
30
+
31
+ def __init__(self, row: Credentials, *args, **kwargs):
32
+ super().__init__(*args, **kwargs)
33
+ self.row = row
34
+
35
+ def compose(self) -> ComposeResult:
36
+ yield Checkbox(
37
+ value=self.row.active, classes="credentials_active", name=self.row.uuid
38
+ )
39
+ yield Label(self.row.name, classes="credentials_name")
40
+ yield Label(self.row.provider, classes="credentials_provider")
41
+ yield Label(self.row.credentials_type, classes="credentials_auth_method")
42
+
43
+ def on_mouse_move(self, _) -> None:
44
+ """Change background color when hovered."""
45
+ self.add_class("active")
46
+
47
+ def on_leave(self, _) -> None:
48
+ """Reset background color when mouse leaves."""
49
+ self.remove_class("active")
50
+
51
+ @on(Checkbox.Changed)
52
+ def on_checkbox_change(self, event: Checkbox.Changed):
53
+ """
54
+ Handle checkbox state changes by updating the row's active status and posting a ChangeActiveStatus message.
55
+
56
+ Args:
57
+ event (Checkbox.Changed): The checkbox change event containing the new value.
58
+ """
59
+ self.row.active = event.value
60
+ self.post_message(self.ChangeActiveStatus(self.row.uuid, self.row.active))
61
+
62
+
63
+ class ProviderCredsListScreen(ModalScreen):
64
+ CSS_PATH = "styles.tcss"
65
+
66
+ MAIN_CONTAINER_ID = "ProviderCredsListScreen"
67
+ SETTINGS_CONTAINER_ID = "settings"
68
+ PROVIDER_SELECTOR_ID = "provider_selector"
69
+ CREDENTIALS_TYPE_SELECTOR_ID = "credentials_type_select"
70
+ CREDENTIALS_FIELDS_CONTAINER_ID = "credentials_fields_container"
71
+
72
+ PROVIDERS_NAME = "providers"
73
+ AUTH_METHODS_NAME = "auth_methods"
74
+
75
+ credentials_list = reactive([], recompose=True)
76
+
77
+ def __init__(self, *args, **kwargs):
78
+ super().__init__(*args, **kwargs)
79
+ self.credentials_service = CredentialsService()
80
+
81
+ def compose(self) -> ComposeResult:
82
+ with Container(id=self.MAIN_CONTAINER_ID):
83
+ yield Container(
84
+ Button(
85
+ "+Add new registration",
86
+ name="add_registration",
87
+ classes="add_registration_button",
88
+ ),
89
+ id="right-top",
90
+ )
91
+ with VerticalScroll(id=self.SETTINGS_CONTAINER_ID):
92
+ with Horizontal():
93
+ yield Label("Active", classes="credentials_active")
94
+ yield Label("Name", classes="credentials_name")
95
+ yield Label("Provider", classes="credentials_provider")
96
+ yield Label("Auth method", classes="credentials_auth_method")
97
+ for row in self.credentials_list:
98
+ yield ProviderCredentialsRow(row, classes="credentials_row")
99
+ with Horizontal(id="controls"):
100
+ yield Button(ControlsEnum.CANCEL.value, name=ControlsEnum.CANCEL.name)
101
+
102
+ def on_compose(self):
103
+ """
104
+ Initialize the screen by refreshing the credentials list when the screen is composed.
105
+ """
106
+ self.refresh_credentials_list()
107
+
108
+ def refresh_credentials_list(self):
109
+ """
110
+ Refresh the credentials list by retrieving the latest credentials from the credentials service.
111
+ """
112
+ self.credentials_list = self.credentials_service.list()
113
+
114
+ def create_provider_creds_registration(
115
+ self, credentials_entry: ProviderCredentialsEntry
116
+ ):
117
+ """
118
+ Create a new provider credentials registration.
119
+
120
+ Stores the provided credentials entry using its associated service and refreshes the credentials list.
121
+
122
+ Args:
123
+ credentials_entry (ProviderCredentialsEntry): The credentials entry to register.
124
+ """
125
+ if not credentials_entry:
126
+ return
127
+ service = credentials_entry.cloud_storage_provider_credentials_service() # type: ignore
128
+ service.store(credentials_entry.name, credentials_entry.fields)
129
+ self.refresh_credentials_list()
130
+
131
+ @on(Button.Click)
132
+ def on_control_button_click(self, event: Button.Click):
133
+ """
134
+ Handle click events for control buttons.
135
+
136
+ Dismisses the screen if the cancel button is clicked, or opens the provider credentials registration screen if
137
+ the add registration button is clicked.
138
+
139
+ Args:
140
+ event (Button.Click): The button click event.
141
+ """
142
+ if event.action == ControlsEnum.CANCEL.name:
143
+ self.dismiss()
144
+ if event.action == "add_registration":
145
+ self.app.push_screen(
146
+ ProviderCredsRegistrationScreen(),
147
+ callback=self.create_provider_creds_registration, # type: ignore
148
+ )
149
+
150
+ @on(ProviderCredentialsRow.ChangeActiveStatus)
151
+ def on_change_active_status(self, event: ProviderCredentialsRow.ChangeActiveStatus):
152
+ """
153
+ Handle changes to the active status of a provider credentials row.
154
+
155
+ Activates or deactivates the credentials based on the event, then refreshes the credentials list.
156
+
157
+ Args:
158
+ event (ProviderCredentialsRow.ChangeActiveStatus): Event containing the credentials UUID and new status.
159
+ """
160
+ if event.active:
161
+ self.credentials_service.activate(event.uuid)
162
+ else:
163
+ self.credentials_service.deactivate(event.uuid)
164
+ self.refresh_credentials_list()
@@ -0,0 +1,65 @@
1
+
2
+ Container {
3
+ height: auto;
4
+ }
5
+
6
+ Label {
7
+ padding-left: 1;
8
+ }
9
+
10
+ ProviderCredsListScreen {
11
+ align: center middle;
12
+ content-align: center top;
13
+
14
+ & > #ProviderCredsListScreen {
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_registration_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
+ .credentials_active {
40
+ width: 10%;
41
+ background: transparent;
42
+ border: none;
43
+ }
44
+
45
+ .credentials_name {
46
+ width: 30%;
47
+ }
48
+
49
+ .credentials_provider {
50
+ width: 30%;
51
+ }
52
+
53
+ .credentials_auth_method {
54
+ width: 30%;
55
+ }
56
+
57
+
58
+ & > Select {
59
+ margin-bottom: 1;
60
+ }
61
+ }
62
+ }
63
+
64
+
65
+