brilliance-admin 0.43.2__tar.gz → 0.43.4__tar.gz

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 (125) hide show
  1. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/PKG-INFO +1 -1
  2. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/fields_schema.py +15 -12
  3. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/base.py +4 -4
  4. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/retrieve.py +3 -0
  5. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/category.py +0 -1
  6. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/base.py +37 -8
  7. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields_schema.py +2 -1
  8. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/translations.py +3 -3
  9. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/utils.py +3 -3
  10. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/PKG-INFO +1 -1
  11. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/locales/en.yml +5 -0
  12. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/locales/ru.yml +5 -0
  13. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/models.py +35 -1
  14. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/payments.py +0 -13
  15. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/terminal.py +1 -0
  16. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/pyproject.toml +1 -1
  17. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_payments_fields_schema.py +0 -29
  18. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_crud.py +18 -1
  19. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_schema.py +28 -0
  20. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/uv.lock +1 -1
  21. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.configs/docker/Dockerfile +0 -0
  22. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.configs/docker/docker-compose.yml +0 -0
  23. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.configs/nginx/example.conf +0 -0
  24. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.env +0 -0
  25. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.github/workflows/certbot.yml +0 -0
  26. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.github/workflows/deploy.yml +0 -0
  27. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.github/workflows/install-docker.yml +0 -0
  28. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.gitignore +0 -0
  29. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.isort.cfg +0 -0
  30. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.python-version +0 -0
  31. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/LICENSE +0 -0
  32. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/README.md +0 -0
  33. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/__init__.py +0 -0
  34. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/__init__.py +0 -0
  35. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/routers.py +0 -0
  36. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/utils.py +0 -0
  37. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/__init__.py +0 -0
  38. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/auth.py +0 -0
  39. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/autocomplete.py +0 -0
  40. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/graphs.py +0 -0
  41. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/index.py +0 -0
  42. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/schema.py +0 -0
  43. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/settings.py +0 -0
  44. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/table.py +0 -0
  45. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/auth.py +0 -0
  46. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/docs.py +0 -0
  47. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/exceptions.py +0 -0
  48. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/__init__.py +0 -0
  49. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/__init__.py +0 -0
  50. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/auth.py +0 -0
  51. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/autocomplete.py +0 -0
  52. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/fields.py +0 -0
  53. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/__init__.py +0 -0
  54. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/create.py +0 -0
  55. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/delete.py +0 -0
  56. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/list.py +0 -0
  57. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/update.py +0 -0
  58. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/locales/en.yml +0 -0
  59. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/locales/ru.yml +0 -0
  60. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/__init__.py +0 -0
  61. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/admin_schema.py +0 -0
  62. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/graphs/__init__.py +0 -0
  63. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/graphs/category_graphs.py +0 -0
  64. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/group.py +0 -0
  65. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/__init__.py +0 -0
  66. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/admin_action.py +0 -0
  67. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/category_table.py +0 -0
  68. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/__init__.py +0 -0
  69. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/function_field.py +0 -0
  70. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/table_models.py +0 -0
  71. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/index-D9axz5zK.js +0 -0
  72. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/index-vlBToOhT.css +0 -0
  73. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
  74. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
  75. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
  76. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
  77. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-first/content.min.css +0 -0
  78. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-first/skin.min.css +0 -0
  79. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-slim/content.min.css +0 -0
  80. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-slim/skin.min.css +0 -0
  81. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/img/example.png +0 -0
  82. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/img/tinymce.woff2 +0 -0
  83. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/lightgray/content.min.css +0 -0
  84. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
  85. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/lightgray/skin.min.css +0 -0
  86. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/accordion/css/accordion.css +0 -0
  87. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/accordion/plugin.js +0 -0
  88. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/codesample/css/prism.css +0 -0
  89. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/customLink/css/link.css +0 -0
  90. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/customLink/plugin.js +0 -0
  91. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/tinymce.min.js +0 -0
  92. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/vanilla-picker-B6E6ObS_.js +0 -0
  93. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/templates/index.html +0 -0
  94. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/SOURCES.txt +0 -0
  95. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/dependency_links.txt +0 -0
  96. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/requires.txt +0 -0
  97. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/top_level.txt +0 -0
  98. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/README.md +0 -0
  99. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/__init__.py +0 -0
  100. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/main.py +0 -0
  101. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/__init__.py +0 -0
  102. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/currency.py +0 -0
  103. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/graphs.py +0 -0
  104. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/merchant.py +0 -0
  105. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/users.py +0 -0
  106. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sqlite.py +0 -0
  107. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/favicon.ico +0 -0
  108. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/favicon.jpg +0 -0
  109. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/logo-outline.png +0 -0
  110. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/logo.png +0 -0
  111. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/utils.py +0 -0
  112. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/PC-graphs.jpeg +0 -0
  113. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/PC-table.jpeg +0 -0
  114. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/iPad-edit.jpeg +0 -0
  115. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/iPhone 15-edit.jpeg +0 -0
  116. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/iPhone 15-login.jpeg +0 -0
  117. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/websitemockupgenerator.png +0 -0
  118. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/setup.cfg +0 -0
  119. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/__init__.py +0 -0
  120. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/conftest.py +0 -0
  121. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_action.py +0 -0
  122. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_settings.py +0 -0
  123. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_auth.py +0 -0
  124. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_filters.py +0 -0
  125. {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_translations.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brilliance-admin
3
- Version: 0.43.2
3
+ Version: 0.43.4
4
4
  Summary: Simple and lightweight admin panel framework powered by FastAPI and Vue3 Vuetify together.. Some call it heavenly in its brilliance.
5
5
  License-Expression: AGPL-3.0
6
6
  Requires-Python: >=3.10
@@ -54,9 +54,6 @@ class SQLAlchemyFieldsSchema(schema.FieldsSchema):
54
54
  and not col.primary_key
55
55
  )
56
56
 
57
- if "choices" in info:
58
- field_data["choices"] = [(c[0], c[1]) for c in info["choices"]]
59
-
60
57
  col_type = col.type
61
58
  try:
62
59
  py_t = col_type.python_type
@@ -64,12 +61,16 @@ class SQLAlchemyFieldsSchema(schema.FieldsSchema):
64
61
  py_t = None
65
62
 
66
63
  impl = getattr(attr, 'impl', None)
67
- is_mutable = isinstance(impl, Mutable)
64
+ is_impl_mutable = isinstance(impl, Mutable)
68
65
 
69
66
  # Foreign key column
70
67
  if col.foreign_keys:
71
68
  continue
72
69
 
70
+ elif "choices" in info:
71
+ field_data["choices"] = info['choices']
72
+ field_class = schema.ChoiceField
73
+
73
74
  elif isinstance(col_type, (sqltypes.BigInteger, sqltypes.Integer)) or py_t is int:
74
75
  field_class = schema.IntegerField
75
76
 
@@ -97,7 +98,7 @@ class SQLAlchemyFieldsSchema(schema.FieldsSchema):
97
98
  elif isinstance(col_type, ARRAY):
98
99
  field_class = schema.ArrayField
99
100
  field_data["array_type"] = type(col_type.item_type).__name__.lower()
100
- field_data["read_only"] = not is_mutable
101
+ field_data["read_only"] = is_impl_mutable or isinstance(col_type, Mutable)
101
102
 
102
103
  elif isinstance(col_type, sqltypes.NullType):
103
104
  continue
@@ -239,15 +240,17 @@ class SQLAlchemyFieldsSchema(schema.FieldsSchema):
239
240
  return stmt
240
241
 
241
242
  async def serialize(self, record, extra: dict, *args, **kwargs) -> dict:
242
- # pylint: disable=import-outside-toplevel
243
- from sqlalchemy import inspect
244
243
 
245
244
  # Convert model values to dict
246
- record_data = {
247
- attr.key: getattr(record, attr.key, None)
248
- for attr in inspect(record).mapper.column_attrs
249
- if self.get_field(attr.key)
250
- }
245
+ record_data = {}
246
+
247
+ for slug, field in self.get_fields().items():
248
+ # pylint: disable=protected-access
249
+ if field._type == 'related':
250
+ record_data[slug] = record
251
+ else:
252
+ record_data[slug] = getattr(record, slug, None)
253
+
251
254
  return await super().serialize(record_data, extra, *args, **kwargs)
252
255
 
253
256
  def validate_incoming_data(self, data):
@@ -123,11 +123,11 @@ class SQLAlchemyAdminBase(SQLAlchemyAdminAutocompleteMixin, CategoryTable):
123
123
  # pylint: disable=protected-access
124
124
  if field._type == "related":
125
125
 
126
- # pylint: disable=import-outside-toplevel
127
- from sqlalchemy import inspect
128
-
129
- model_attrs = [attr.key for attr in inspect(self.model).mapper.attrs]
130
126
  if not hasattr(self.model, field.rel_name):
127
+ # pylint: disable=import-outside-toplevel
128
+ from sqlalchemy import inspect
129
+ model_attrs = [attr.key for attr in inspect(self.model).mapper.attrs]
130
+
131
131
  msg = EXCEPTION_REL_NAME.format(
132
132
  slug=slug,
133
133
  model_name=self.model.__name__,
@@ -2,6 +2,7 @@ from typing import Any
2
2
 
3
3
  from brilliance_admin import auth, schema
4
4
  from brilliance_admin.exceptions import AdminAPIException, APIError
5
+ from brilliance_admin.integrations.sqlalchemy.fields_schema import SQLAlchemyFieldsSchema
5
6
  from brilliance_admin.translations import LanguageContext
6
7
  from brilliance_admin.translations import TranslateText as _
7
8
  from brilliance_admin.utils import get_logger
@@ -12,6 +13,8 @@ logger = get_logger()
12
13
  class SQLAlchemyAdminRetrieveMixin:
13
14
  has_retrieve: bool = True
14
15
 
16
+ table_schema: SQLAlchemyFieldsSchema
17
+
15
18
  async def retrieve(
16
19
  self,
17
20
  pk: Any,
@@ -30,7 +30,6 @@ class FieldSchemaData(DataclassBase):
30
30
 
31
31
  choices: List[dict] | None = None
32
32
 
33
- tag_colors: dict | None = None
34
33
  variant: str | None = None
35
34
  size: str | None = None
36
35
 
@@ -1,6 +1,7 @@
1
1
  import abc
2
2
  import datetime
3
- from typing import Any, ClassVar, List, Tuple
3
+ from enum import Enum
4
+ from typing import Any, ClassVar
4
5
 
5
6
  from pydantic.dataclasses import dataclass
6
7
 
@@ -52,6 +53,8 @@ class TableField(abc.ABC, FieldSchemaData):
52
53
  class IntegerField(TableField):
53
54
  _type = 'integer'
54
55
 
56
+ choices: Any | None = None
57
+
55
58
  max_value: int | None = None
56
59
  min_value: int | None = None
57
60
 
@@ -62,8 +65,6 @@ class IntegerField(TableField):
62
65
  def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
63
66
  schema = super().generate_schema(user, field_slug, language_context)
64
67
 
65
- schema.choices = self.choices
66
-
67
68
  if self.max_value is not None:
68
69
  schema.max_value = self.max_value
69
70
 
@@ -95,13 +96,12 @@ class StringField(TableField):
95
96
  min_length: int | None = None
96
97
  max_length: int | None = None
97
98
 
98
- choices: List[Tuple[str, str]] | None = None
99
+ choices: Any | None = None
99
100
 
100
101
  def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
101
102
  schema = super().generate_schema(user, field_slug, language_context)
102
103
 
103
104
  schema.multilined = self.multilined
104
- schema.choices = self.choices
105
105
  schema.ckeditor = self.ckeditor
106
106
  schema.tinymce = self.tinymce
107
107
 
@@ -227,23 +227,52 @@ class ImageField(TableField):
227
227
  class ChoiceField(TableField):
228
228
  _type = 'choice'
229
229
 
230
+ # Tag color available:
230
231
  # https://vuetifyjs.com/en/styles/colors/#classes
231
- tag_colors: dict | None = None
232
+ choices: Any | None = None
232
233
 
233
234
  # https://vuetifyjs.com/en/components/chips/#color-and-variants
234
235
  variant: str = 'elevated'
235
236
  size: str = 'default'
236
237
 
238
+ def __post_init__(self):
239
+ self.choices = self.generate_choices()
240
+
241
+ def generate_choices(self):
242
+ if not self.choices:
243
+ return None
244
+
245
+ if issubclass(self.choices, Enum):
246
+ return [
247
+ {'value': c.value, 'title': c.label, 'tag_color': getattr(c, 'tag_color', None)}
248
+ for c in self.choices
249
+ ]
250
+
251
+ msg = f'Field choices is not suppored: {self.choices}'
252
+ raise NotImplementedError(msg)
253
+
254
+ def find_choice(self, value):
255
+ if not self.choices:
256
+ return None
257
+
258
+ return next((c for c in self.choices if c.get('value') == value), None)
259
+
237
260
  def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
238
261
  schema = super().generate_schema(user, field_slug, language_context)
239
262
 
240
263
  schema.choices = self.choices
241
264
 
242
- schema.tag_colors = self.tag_colors
243
265
  schema.size = self.size
244
266
  schema.variant = self.variant
245
267
 
246
268
  return schema
247
269
 
248
270
  async def serialize(self, value, extra: dict, *args, **kwargs) -> Any:
249
- return {'value': value, 'title': value.capitalize() if value else value}
271
+ if not value:
272
+ return
273
+
274
+ choice = self.find_choice(value)
275
+ return {
276
+ 'value': value,
277
+ 'title': choice.get('title') or value if choice else value.capitalize(),
278
+ }
@@ -148,9 +148,10 @@ class FieldsSchema:
148
148
  list_display=self.list_display,
149
149
  )
150
150
 
151
+ context = {'language_context': language_context}
151
152
  for field_slug, field in self.get_fields().items():
152
153
  field_schema: FieldSchemaData = field.generate_schema(user, field_slug, language_context)
153
- fields_schema.fields[field_slug] = field_schema.to_dict(keep_none=False)
154
+ fields_schema.fields[field_slug] = field_schema.to_dict(keep_none=False, context=context)
154
155
 
155
156
  return fields_schema
156
157
 
@@ -63,14 +63,14 @@ class LanguageManager(abc.ABC):
63
63
 
64
64
  builtin_locales_dir = resources.files("brilliance_admin").joinpath("locales")
65
65
  self.phrases.load_folder(builtin_locales_dir)
66
- logger.info('Language manager builtin dir loaded: %s', builtin_locales_dir)
66
+ logger.debug('Language manager builtin dir loaded: %s', builtin_locales_dir)
67
67
 
68
68
  if locales_dir:
69
69
  self.phrases.load_folder(locales_dir)
70
- logger.info('Language manager locales_dir loaded: %s', locales_dir)
70
+ logger.debug('Language manager locales_dir loaded: %s', locales_dir)
71
71
 
72
72
  langs = ', '.join(self.phrases.data.keys())
73
- logger.info('Language manager setup completed; languages: %s', langs)
73
+ logger.debug('Language manager setup completed; languages: %s', langs)
74
74
 
75
75
  def get_text(self, text, language) -> str:
76
76
  if not isinstance(text, TranslateText):
@@ -42,8 +42,8 @@ class DataclassBase:
42
42
  adapter = TypeAdapter(type(self))
43
43
  return adapter.dump_python(self, *args, **kwargs)
44
44
 
45
- def to_dict(self, keep_none=True) -> dict:
46
- data = self.model_dump()
45
+ def to_dict(self, *args, keep_none=True, **kwargs) -> dict:
46
+ data = self.model_dump(*args, **kwargs)
47
47
  return {
48
48
  k: v for k, v in data.items()
49
49
  if v is not None and not keep_none
@@ -150,4 +150,4 @@ class YamlI18n:
150
150
  if isinstance(node, str):
151
151
  return node
152
152
 
153
- raise KeyError(f'translation key not found: {slug}')
153
+ return slug
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brilliance-admin
3
- Version: 0.43.2
3
+ Version: 0.43.4
4
4
  Summary: Simple and lightweight admin panel framework powered by FastAPI and Vue3 Vuetify together.. Some call it heavenly in its brilliance.
5
5
  License-Expression: AGPL-3.0
6
6
  Requires-Python: >=3.10
@@ -50,3 +50,8 @@ whitelist_ips: 'Whitelist IPs'
50
50
  users: 'Users'
51
51
  field_not_found_in_schema: 'The field "{field_slug}" was not found among the available fields: {available}'
52
52
  related_not_found: 'Error updating related field {field_slug}: {model} record with pk={pk} was not found. The record may no longer be available.'
53
+
54
+ statuses:
55
+ process: 'Process'
56
+ success: 'Success'
57
+ error: 'Error'
@@ -49,3 +49,8 @@ whitelist_ips: 'Белый список IP'
49
49
  users: 'Пользователи'
50
50
  field_not_found_in_schema: 'Поле "{field_slug}" не найдено среди доступных: {available}'
51
51
  related_not_found: 'Ошибка при обновлении связей поля {field_slug}: запись {model} с ключем pk={pk} не найдена. Возможно запись более недоступна.'
52
+
53
+ statuses:
54
+ process: 'В процессе'
55
+ success: 'Успех'
56
+ error: 'Ошибка'
@@ -1,5 +1,7 @@
1
+ import random
1
2
  import uuid
2
3
  from datetime import datetime
4
+ from enum import Enum
3
5
 
4
6
  import factory
5
7
  from sqlalchemy import BigInteger, Boolean, DateTime, ForeignKey, Integer, SmallInteger, String, func
@@ -131,7 +133,7 @@ class Fee(BaseIDModel):
131
133
 
132
134
  class FeeFactory(SQLAlchemyFactoryBase):
133
135
  class Meta:
134
- model = Merchant
136
+ model = Fee
135
137
  sqlalchemy_session_factory = async_sessionmaker_
136
138
  sqlalchemy_session_persistence = "commit"
137
139
 
@@ -141,6 +143,30 @@ class FeeFactory(SQLAlchemyFactoryBase):
141
143
  return self.title
142
144
 
143
145
 
146
+ class TerminalStatuses(Enum):
147
+ PROCESS = 'process'
148
+ SUCCESS = 'success'
149
+ ERROR = 'error'
150
+
151
+ @property
152
+ def label(self):
153
+ labels = {
154
+ self.PROCESS: _('statuses.process'),
155
+ self.SUCCESS: _('statuses.success'),
156
+ self.ERROR: _('statuses.error'),
157
+ }
158
+ return labels[self]
159
+
160
+ @property
161
+ def tag_color(self):
162
+ colors = {
163
+ self.PROCESS: 'grey-lighten-1',
164
+ self.SUCCESS: 'green-darken-1',
165
+ self.ERROR: 'red-lighten-2',
166
+ }
167
+ return colors[self]
168
+
169
+
144
170
  class Terminal(BaseIDModel):
145
171
  __tablename__ = "terminal"
146
172
 
@@ -156,6 +182,12 @@ class Terminal(BaseIDModel):
156
182
  default=lambda: str(uuid.uuid4()),
157
183
  )
158
184
 
185
+ status: Mapped[str] = mapped_column(
186
+ String(255),
187
+ nullable=False,
188
+ info={"choices": TerminalStatuses},
189
+ )
190
+
159
191
  return_url: Mapped[str] = mapped_column(String(500), nullable=True)
160
192
  callback_url: Mapped[str] = mapped_column(String(500), nullable=True)
161
193
 
@@ -204,6 +236,8 @@ class TerminalFactory(SQLAlchemyFactoryBase):
204
236
  title = factory.Faker("company")
205
237
  description = factory.Faker("sentence", nb_words=6)
206
238
 
239
+ status = factory.LazyFunction(lambda: random.choice(list(TerminalStatuses)).value)
240
+
207
241
  secret_key = factory.LazyFunction(lambda: str(uuid.uuid4()))
208
242
  public_id = factory.LazyFunction(lambda: str(uuid.uuid4()))
209
243
 
@@ -25,13 +25,6 @@ class PaymentFiltersSchema(schema.FieldsSchema):
25
25
  ]
26
26
 
27
27
 
28
- STATUS_COLORS = {
29
- 'process': 'grey-lighten-1',
30
- 'success': 'green-darken-1',
31
- 'error': 'red-lighten-2',
32
- }
33
-
34
-
35
28
  class PaymentFieldsSchema(schema.FieldsSchema):
36
29
  id = schema.IntegerField(label='ID', read_only=True)
37
30
  amount = schema.IntegerField(label=_('amount'))
@@ -39,11 +32,6 @@ class PaymentFieldsSchema(schema.FieldsSchema):
39
32
  description = schema.StringField(label=_('description'))
40
33
  other_field = schema.StringField(read_only=True)
41
34
  whitelist_ips = schema.ArrayField(label=_('whitelist_ips'))
42
- status = schema.ChoiceField(
43
- label=_('status'),
44
- tag_colors=STATUS_COLORS,
45
- choices=list({'value': k, 'title': k.capitalize()} for k in STATUS_COLORS),
46
- )
47
35
  # image = schema.ImageField(label=_('image'))
48
36
  created_at = schema.DateTimeField(label=_('created_at'), read_only=True)
49
37
 
@@ -51,7 +39,6 @@ class PaymentFieldsSchema(schema.FieldsSchema):
51
39
  'id',
52
40
  'amount',
53
41
  'endpoint',
54
- 'status',
55
42
  'description',
56
43
  'created_at',
57
44
  'get_provider_registry',
@@ -20,6 +20,7 @@ class TerminalAdmin(sqlalchemy.SQLAlchemyAdmin):
20
20
  model=Terminal,
21
21
  list_display=[
22
22
  'id',
23
+ 'status',
23
24
  'merchant_id',
24
25
  'public_id',
25
26
  'currency_id',
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brilliance-admin"
3
- version = "0.43.2"
3
+ version = "0.43.4"
4
4
  description = 'Simple and lightweight admin panel framework powered by FastAPI and Vue3 Vuetify together.. Some call it heavenly in its brilliance.'
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -160,34 +160,6 @@ category_schema_data = {
160
160
  'required': False,
161
161
  'type': 'string',
162
162
  },
163
- 'status': {
164
- 'choices': [
165
- {
166
- 'title': 'Process',
167
- 'value': 'process',
168
- },
169
- {
170
- 'title': 'Success',
171
- 'value': 'success',
172
- },
173
- {
174
- 'title': 'Error',
175
- 'value': 'error',
176
- },
177
- ],
178
- 'header': {},
179
- 'label': 'Статус',
180
- 'read_only': False,
181
- 'required': False,
182
- 'size': 'default',
183
- 'tag_colors': {
184
- 'error': 'red-lighten-2',
185
- 'process': 'grey-lighten-1',
186
- 'success': 'green-darken-1',
187
- },
188
- 'type': 'choice',
189
- 'variant': 'elevated',
190
- },
191
163
  'whitelist_ips': {
192
164
  'header': {},
193
165
  'label': 'Белый список IP',
@@ -200,7 +172,6 @@ category_schema_data = {
200
172
  'id',
201
173
  'amount',
202
174
  'endpoint',
203
- 'status',
204
175
  'description',
205
176
  'created_at',
206
177
  'get_provider_registry',
@@ -5,7 +5,9 @@ from sqlalchemy import select
5
5
  from sqlalchemy.orm import selectinload
6
6
 
7
7
  from brilliance_admin import auth, schema, sqlalchemy
8
- from example.sections.models import Currency, CurrencyFactory, MerchantFactory, Terminal, TerminalFactory
8
+ from brilliance_admin.translations import TranslateText
9
+ from example.sections.models import (
10
+ Currency, CurrencyFactory, MerchantFactory, Terminal, TerminalFactory, TerminalStatuses)
9
11
  from tests.test_sqlalcmeny_schema import FIELDS
10
12
 
11
13
 
@@ -31,6 +33,7 @@ async def test_create(sqlite_sessionmaker, language_context):
31
33
  create_data = {
32
34
  'merchant_id': merchant.id,
33
35
  'currency_id': currency.id,
36
+ 'status': 'test',
34
37
  'description': 'test',
35
38
  'title': 'test',
36
39
  }
@@ -52,6 +55,7 @@ async def test_retrieve(sqlite_sessionmaker, language_context):
52
55
  terminal = await TerminalFactory(
53
56
  title="test",
54
57
  description='test',
58
+ status=TerminalStatuses.PROCESS.value,
55
59
  is_h2h=False,
56
60
  registered_delay=None,
57
61
  merchant=merchant,
@@ -70,6 +74,10 @@ async def test_retrieve(sqlite_sessionmaker, language_context):
70
74
  'key': currency.id,
71
75
  'title': mock.ANY,
72
76
  },
77
+ 'status': {
78
+ 'title': TranslateText('statuses.process'),
79
+ 'value': 'process',
80
+ },
73
81
  'title': 'test',
74
82
  'id': terminal.id,
75
83
  'is_h2h': False,
@@ -93,6 +101,10 @@ async def test_retrieve_currency(sqlite_sessionmaker, language_context):
93
101
  ],
94
102
  ),
95
103
  )
104
+ terminals_field = category.table_schema.get_field('terminals')
105
+ assert terminals_field._type == "related"
106
+ assert terminals_field.rel_name == "terminals"
107
+ assert terminals_field.many is True
96
108
 
97
109
  user = auth.UserABC(username="test")
98
110
  merchant = await MerchantFactory()
@@ -132,6 +144,7 @@ async def test_list(sqlite_sessionmaker, language_context):
132
144
  registered_delay=None,
133
145
  title='Test terminal',
134
146
  description="description",
147
+ status=TerminalStatuses.PROCESS.value,
135
148
  merchant=await MerchantFactory(title="Test merch"),
136
149
  currency=await CurrencyFactory(),
137
150
  )
@@ -152,6 +165,10 @@ async def test_list(sqlite_sessionmaker, language_context):
152
165
  'key': 1,
153
166
  'title': mock.ANY,
154
167
  },
168
+ 'status': {
169
+ 'title': TranslateText('statuses.process'),
170
+ 'value': 'process',
171
+ },
155
172
  'description': 'description',
156
173
  'id': 1,
157
174
  'is_h2h': False,
@@ -110,10 +110,37 @@ category_schema_data = {
110
110
  'required': True,
111
111
  'type': 'string',
112
112
  },
113
+ 'status': {
114
+ 'choices': [
115
+ {
116
+ 'tag_color': 'grey-lighten-1',
117
+ 'title': 'В процессе',
118
+ 'value': 'process',
119
+ },
120
+ {
121
+ 'tag_color': 'green-darken-1',
122
+ 'title': 'Успех',
123
+ 'value': 'success',
124
+ },
125
+ {
126
+ 'tag_color': 'red-lighten-2',
127
+ 'title': 'Ошибка',
128
+ 'value': 'error',
129
+ },
130
+ ],
131
+ 'header': {},
132
+ 'label': 'Status',
133
+ 'read_only': False,
134
+ 'required': True,
135
+ 'size': 'default',
136
+ 'type': 'choice',
137
+ 'variant': 'elevated',
138
+ },
113
139
  },
114
140
  'list_display': [
115
141
  'id',
116
142
  'title',
143
+ 'status',
117
144
  'description',
118
145
  'secret_key',
119
146
  'currency_id',
@@ -131,6 +158,7 @@ category_schema_data = {
131
158
  FIELDS = [
132
159
  'id',
133
160
  'title',
161
+ 'status',
134
162
  'description',
135
163
  'secret_key',
136
164
  'currency_id',
@@ -65,7 +65,7 @@ wheels = [
65
65
 
66
66
  [[package]]
67
67
  name = "brilliance-admin"
68
- version = "0.41.2"
68
+ version = "0.43.4"
69
69
  source = { editable = "." }
70
70
  dependencies = [
71
71
  { name = "asgiref" },