brilliance-admin 0.42.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 (73) hide show
  1. admin_panel/__init__.py +4 -0
  2. admin_panel/api/__init__.py +0 -0
  3. admin_panel/api/routers.py +18 -0
  4. admin_panel/api/utils.py +28 -0
  5. admin_panel/api/views/__init__.py +0 -0
  6. admin_panel/api/views/auth.py +29 -0
  7. admin_panel/api/views/autocomplete.py +33 -0
  8. admin_panel/api/views/graphs.py +30 -0
  9. admin_panel/api/views/index.py +38 -0
  10. admin_panel/api/views/schema.py +29 -0
  11. admin_panel/api/views/settings.py +29 -0
  12. admin_panel/api/views/table.py +136 -0
  13. admin_panel/auth.py +32 -0
  14. admin_panel/docs.py +37 -0
  15. admin_panel/exceptions.py +38 -0
  16. admin_panel/integrations/__init__.py +0 -0
  17. admin_panel/integrations/sqlalchemy/__init__.py +6 -0
  18. admin_panel/integrations/sqlalchemy/auth.py +144 -0
  19. admin_panel/integrations/sqlalchemy/autocomplete.py +38 -0
  20. admin_panel/integrations/sqlalchemy/fields.py +254 -0
  21. admin_panel/integrations/sqlalchemy/fields_schema.py +316 -0
  22. admin_panel/integrations/sqlalchemy/table/__init__.py +19 -0
  23. admin_panel/integrations/sqlalchemy/table/base.py +141 -0
  24. admin_panel/integrations/sqlalchemy/table/create.py +73 -0
  25. admin_panel/integrations/sqlalchemy/table/delete.py +18 -0
  26. admin_panel/integrations/sqlalchemy/table/list.py +178 -0
  27. admin_panel/integrations/sqlalchemy/table/retrieve.py +61 -0
  28. admin_panel/integrations/sqlalchemy/table/update.py +95 -0
  29. admin_panel/schema/__init__.py +7 -0
  30. admin_panel/schema/admin_schema.py +191 -0
  31. admin_panel/schema/category.py +149 -0
  32. admin_panel/schema/graphs/__init__.py +1 -0
  33. admin_panel/schema/graphs/category_graphs.py +50 -0
  34. admin_panel/schema/group.py +67 -0
  35. admin_panel/schema/table/__init__.py +8 -0
  36. admin_panel/schema/table/admin_action.py +76 -0
  37. admin_panel/schema/table/category_table.py +175 -0
  38. admin_panel/schema/table/fields/__init__.py +5 -0
  39. admin_panel/schema/table/fields/base.py +249 -0
  40. admin_panel/schema/table/fields/function_field.py +65 -0
  41. admin_panel/schema/table/fields_schema.py +216 -0
  42. admin_panel/schema/table/table_models.py +53 -0
  43. admin_panel/static/favicon.jpg +0 -0
  44. admin_panel/static/index-BeniOHDv.js +525 -0
  45. admin_panel/static/index-vlBToOhT.css +8 -0
  46. admin_panel/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
  47. admin_panel/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
  48. admin_panel/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
  49. admin_panel/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
  50. admin_panel/static/tinymce/dark-first/content.min.css +250 -0
  51. admin_panel/static/tinymce/dark-first/skin.min.css +2820 -0
  52. admin_panel/static/tinymce/dark-slim/content.min.css +249 -0
  53. admin_panel/static/tinymce/dark-slim/skin.min.css +2821 -0
  54. admin_panel/static/tinymce/img/example.png +0 -0
  55. admin_panel/static/tinymce/img/tinymce.woff2 +0 -0
  56. admin_panel/static/tinymce/lightgray/content.min.css +1 -0
  57. admin_panel/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
  58. admin_panel/static/tinymce/lightgray/skin.min.css +1 -0
  59. admin_panel/static/tinymce/plugins/accordion/css/accordion.css +17 -0
  60. admin_panel/static/tinymce/plugins/accordion/plugin.js +48 -0
  61. admin_panel/static/tinymce/plugins/codesample/css/prism.css +1 -0
  62. admin_panel/static/tinymce/plugins/customLink/css/link.css +3 -0
  63. admin_panel/static/tinymce/plugins/customLink/plugin.js +147 -0
  64. admin_panel/static/tinymce/tinymce.min.js +2 -0
  65. admin_panel/static/vanilla-picker-B6E6ObS_.js +8 -0
  66. admin_panel/templates/index.html +25 -0
  67. admin_panel/translations.py +145 -0
  68. admin_panel/utils.py +50 -0
  69. brilliance_admin-0.42.0.dist-info/METADATA +155 -0
  70. brilliance_admin-0.42.0.dist-info/RECORD +73 -0
  71. brilliance_admin-0.42.0.dist-info/WHEEL +5 -0
  72. brilliance_admin-0.42.0.dist-info/licenses/LICENSE +17 -0
  73. brilliance_admin-0.42.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,216 @@
1
+ import asyncio
2
+ from typing import Any, ClassVar, Dict, List
3
+
4
+ from pydantic_core import core_schema
5
+
6
+ from admin_panel.auth import UserABC
7
+ from admin_panel.exceptions import AdminAPIException, APIError, FieldError
8
+ from admin_panel.schema.category import FieldSchemaData, FieldsSchemaData
9
+ from admin_panel.schema.table.fields.base import TableField
10
+ from admin_panel.schema.table.fields.function_field import FunctionField
11
+ from admin_panel.translations import LanguageManager
12
+ from admin_panel.utils import DeserializeAction
13
+
14
+ NOT_FUND_EXCEPTION = '''Field slug "{field_slug}" not found inside generated fields inside {class_name}
15
+ Available options: {available_fields}
16
+ '''
17
+
18
+
19
+ class DeserializeError(Exception):
20
+ pass
21
+
22
+
23
+ class FieldsSchema:
24
+ # Список полей
25
+ fields: List[str] | None = None
26
+
27
+ # Список колонок, которые будут отображаться в таблице
28
+ list_display: List[str] | None = None
29
+
30
+ # Для передачи параметра read_only = True внутрь поля
31
+ readonly_fields: ClassVar[List | None] = None
32
+
33
+ # Generated fields
34
+ _generated_fields: dict = None
35
+
36
+ def __init__(self, *args, table_schema=None, list_display=None, readonly_fields=None, fields=None, **kwargs):
37
+ if fields:
38
+ self.fields = fields
39
+
40
+ if self.fields and not isinstance(self.fields, list):
41
+ msg = f'{type(self).__name__}.fields must be a list instance; found: {self.fields}'
42
+ raise AttributeError(msg)
43
+
44
+ if table_schema:
45
+ self.table_schema = table_schema
46
+
47
+ if list_display:
48
+ self.list_display = list_display
49
+
50
+ if readonly_fields:
51
+ self.readonly_fields = readonly_fields
52
+
53
+ generated_fields = self.generate_fields(kwargs)
54
+
55
+ available_fields = list(generated_fields.keys())
56
+ if self.fields is None:
57
+ self.fields = available_fields
58
+
59
+ self._generated_fields = {}
60
+ for field_slug in self.fields:
61
+ if not isinstance(field_slug, str):
62
+ msg = f'{type(self).__name__} field "{field_slug}" must be string'
63
+ raise AttributeError(msg)
64
+
65
+ if field_slug not in generated_fields:
66
+ msg = NOT_FUND_EXCEPTION.format(
67
+ field_slug=field_slug,
68
+ available_fields=available_fields,
69
+ class_name=type(self).__name__,
70
+ )
71
+ raise AttributeError(msg)
72
+
73
+ self._generated_fields[field_slug] = generated_fields[field_slug]
74
+
75
+ self.validate_fields(*args, **kwargs)
76
+
77
+ def validate_fields(self, *args, **kwargs):
78
+ if not self.fields:
79
+ msg = f'Schema {type(self).__name__}.fields is empty'
80
+ raise AttributeError(msg)
81
+
82
+ # Check for fields not listed in self.fields
83
+ for attribute_name in dir(self):
84
+ if '__' in attribute_name:
85
+ continue
86
+
87
+ attribute = getattr(self, attribute_name)
88
+ if issubclass(attribute.__class__, TableField) and attribute_name not in self.fields:
89
+ msg = f'Schema {type(self).__name__} attribute "{attribute_name}" {type(attribute).__name__} presented, but not listed inside fields list: {self.fields}'
90
+ raise AttributeError(msg)
91
+
92
+ if self.readonly_fields:
93
+ for field_slug in self.readonly_fields:
94
+ field = self.get_field(field_slug)
95
+ if not field:
96
+ msg = f'{type(self).__name__} field "{field_slug}" from readonly_fields is not found inside fields; available options: {self.fields}'
97
+ raise AttributeError(msg)
98
+
99
+ field.read_only = True
100
+
101
+ # Fill list_display
102
+ if self.list_display is None:
103
+ self.list_display = self.fields
104
+
105
+ for field_slug in self.list_display:
106
+ if field_slug not in self.fields:
107
+ msg = f'Field "{field_slug}" inside {type(self).__name__}.list_display, but not presented as field; available options: {self.fields}'
108
+ raise AttributeError(msg)
109
+
110
+ def generate_fields(self, kwargs) -> dict:
111
+ generated_fields = {}
112
+
113
+ # Fields from kwargs
114
+ for k, v in kwargs.items():
115
+ if issubclass(type(v), TableField) and not hasattr(self, k):
116
+ generated_fields[k] = v
117
+
118
+ # Autogenerate fields from instance attributes
119
+ for attribute_name in dir(self):
120
+ if '__' in attribute_name:
121
+ continue
122
+
123
+ attribute = getattr(self, attribute_name)
124
+ if issubclass(type(attribute), TableField):
125
+ generated_fields[attribute_name] = attribute
126
+
127
+ # Generation FunctionField
128
+ for attribute_name in dir(self):
129
+ if '__' in attribute_name:
130
+ continue
131
+
132
+ attribute = getattr(self, attribute_name)
133
+ if getattr(attribute, '__function_field__', False):
134
+ field = FunctionField(fn=attribute, **attribute.__kwargs__)
135
+ field.read_only = True
136
+ generated_fields[attribute_name] = field
137
+
138
+ return generated_fields
139
+
140
+ def get_field(self, field_slug) -> TableField | None:
141
+ return self.get_fields().get(field_slug)
142
+
143
+ def get_fields(self) -> Dict[str, TableField]:
144
+ return self._generated_fields
145
+
146
+ def generate_schema(self, user: UserABC, language_manager: LanguageManager) -> FieldsSchemaData:
147
+ fields_schema = FieldsSchemaData(
148
+ list_display=self.list_display,
149
+ )
150
+
151
+ for field_slug, field in self.get_fields().items():
152
+ field_schema: FieldSchemaData = field.generate_schema(user, field_slug, language_manager)
153
+ fields_schema.fields[field_slug] = field_schema.to_dict(keep_none=False)
154
+
155
+ return fields_schema
156
+
157
+ async def serialize(self, data: Any, extra: dict) -> dict:
158
+ result = {}
159
+ for field_slug, field in self.get_fields().items():
160
+ value = data.get(field_slug)
161
+ result[field_slug] = await field.serialize(value, extra)
162
+ return result
163
+
164
+ async def deserialize(self, data: dict, action: DeserializeAction, extra) -> dict:
165
+ result = {}
166
+ errors = {}
167
+ for field_slug, field in self.get_fields().items():
168
+
169
+ if field.read_only and action != DeserializeAction.FILTERS:
170
+ continue
171
+
172
+ # Skip update if fields is not presented in data
173
+ if action in [DeserializeAction.UPDATE, DeserializeAction.FILTERS] and field_slug not in data:
174
+ continue
175
+
176
+ value = data.get(field_slug)
177
+ try:
178
+ deserialized_value = await field.deserialize(value, action, extra)
179
+
180
+ validate_method = getattr(self, f'validate_{field_slug}', None)
181
+ if callable(validate_method):
182
+ if not asyncio.iscoroutinefunction(validate_method):
183
+ msg = f'Validate method {type(self).__name__}.{field_slug} must be async'
184
+ raise AttributeError(msg)
185
+ deserialized_value = await validate_method(value)
186
+
187
+ field.set_deserialized_value(result, field_slug, deserialized_value, action, extra)
188
+ except FieldError as e:
189
+ errors[field_slug] = e
190
+
191
+ if errors:
192
+ raise AdminAPIException(
193
+ APIError(
194
+ message='Validation error',
195
+ code='validation_error',
196
+ field_errors=errors,
197
+ ),
198
+ status_code=400,
199
+ )
200
+ return result
201
+
202
+ @classmethod
203
+ def __get_pydantic_core_schema__(cls, source_type: Any, handler: Any) -> core_schema.CoreSchema:
204
+ def validate(v: Any) -> "FieldsSchema":
205
+ if isinstance(v, cls):
206
+ return v
207
+ raise TypeError(f"Expected {cls.__name__} instance")
208
+
209
+ return core_schema.no_info_plain_validator_function(
210
+ validate,
211
+ serialization=core_schema.plain_serializer_function_ser_schema(
212
+ lambda v: repr(v),
213
+ info_arg=False,
214
+ return_schema=core_schema.str_schema(),
215
+ ),
216
+ )
@@ -0,0 +1,53 @@
1
+ from typing import Any, Dict, List
2
+
3
+ from pydantic import BaseModel, Field
4
+ from pydantic.dataclasses import dataclass
5
+
6
+ from admin_panel.utils import DataclassBase
7
+
8
+
9
+ @dataclass
10
+ class TableListResult(DataclassBase):
11
+ data: List[dict]
12
+ total_count: int
13
+
14
+
15
+ class AutocompleteData(BaseModel):
16
+ field_slug: str
17
+ search_string: str = ''
18
+ is_filter: bool = False
19
+ form_data: dict = Field(default_factory=dict)
20
+ existed_choices: List[Any] = Field(default_factory=list)
21
+ action_name: str | None = None
22
+ limit: int = 25
23
+
24
+
25
+ class Record(BaseModel):
26
+ key: Any
27
+ title: str
28
+
29
+
30
+ class AutocompleteResult(BaseModel):
31
+ results: List[Record] = Field(default_factory=list)
32
+
33
+
34
+ class ListData(BaseModel):
35
+ page: int = 1
36
+ limit: int = 25
37
+
38
+ search: str | None = None
39
+ filters: Dict[str, Any] = Field(default_factory=dict)
40
+
41
+ ordering: str | None = None
42
+
43
+
44
+ class RetrieveResult(BaseModel):
45
+ data: dict
46
+
47
+
48
+ class CreateResult(BaseModel):
49
+ pk: Any
50
+
51
+
52
+ class UpdateResult(BaseModel):
53
+ pk: Any
Binary file