brilliance-admin 0.43.1__py3-none-any.whl → 0.44.12__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 (41) hide show
  1. brilliance_admin/api/routers.py +2 -2
  2. brilliance_admin/api/views/autocomplete.py +1 -1
  3. brilliance_admin/api/views/{graphs.py → dashboard.py} +5 -5
  4. brilliance_admin/api/views/table.py +7 -6
  5. brilliance_admin/exceptions.py +1 -0
  6. brilliance_admin/integrations/sqlalchemy/__init__.py +1 -0
  7. brilliance_admin/integrations/sqlalchemy/auth.py +3 -4
  8. brilliance_admin/integrations/sqlalchemy/autocomplete.py +12 -3
  9. brilliance_admin/integrations/sqlalchemy/fields.py +36 -19
  10. brilliance_admin/integrations/sqlalchemy/fields_schema.py +21 -12
  11. brilliance_admin/integrations/sqlalchemy/table/base.py +4 -4
  12. brilliance_admin/integrations/sqlalchemy/table/create.py +6 -3
  13. brilliance_admin/integrations/sqlalchemy/table/delete.py +2 -2
  14. brilliance_admin/integrations/sqlalchemy/table/list.py +44 -14
  15. brilliance_admin/integrations/sqlalchemy/table/retrieve.py +41 -10
  16. brilliance_admin/integrations/sqlalchemy/table/update.py +10 -5
  17. brilliance_admin/locales/en.yml +20 -10
  18. brilliance_admin/locales/ru.yml +20 -10
  19. brilliance_admin/schema/__init__.py +3 -3
  20. brilliance_admin/schema/admin_schema.py +31 -23
  21. brilliance_admin/schema/category.py +88 -14
  22. brilliance_admin/schema/dashboard/__init__.py +1 -0
  23. brilliance_admin/schema/dashboard/category_dashboard.py +87 -0
  24. brilliance_admin/schema/table/category_table.py +13 -8
  25. brilliance_admin/schema/table/fields/base.py +102 -19
  26. brilliance_admin/schema/table/fields_schema.py +9 -2
  27. brilliance_admin/static/{index-D9axz5zK.js → index-8ahvKI6W.js} +190 -190
  28. brilliance_admin/static/{index-vlBToOhT.css → index-B8JOx1Ps.css} +1 -1
  29. brilliance_admin/templates/index.html +2 -2
  30. brilliance_admin/translations.py +6 -3
  31. brilliance_admin/utils.py +41 -3
  32. brilliance_admin-0.44.12.dist-info/METADATA +165 -0
  33. {brilliance_admin-0.43.1.dist-info → brilliance_admin-0.44.12.dist-info}/RECORD +36 -37
  34. {brilliance_admin-0.43.1.dist-info → brilliance_admin-0.44.12.dist-info}/WHEEL +1 -1
  35. brilliance_admin-0.44.12.dist-info/licenses/LICENSE +21 -0
  36. brilliance_admin/schema/graphs/__init__.py +0 -1
  37. brilliance_admin/schema/graphs/category_graphs.py +0 -51
  38. brilliance_admin/schema/group.py +0 -67
  39. brilliance_admin-0.43.1.dist-info/METADATA +0 -214
  40. brilliance_admin-0.43.1.dist-info/licenses/LICENSE +0 -17
  41. {brilliance_admin-0.43.1.dist-info → brilliance_admin-0.44.12.dist-info}/top_level.txt +0 -0
@@ -13,8 +13,8 @@
13
13
  <link rel="icon" href="{{ favicon_image }}" />
14
14
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
15
15
  <title>{{ title }}</title>
16
- <script type="module" crossorigin src="/admin/static/index-D9axz5zK.js"></script>
17
- <link rel="stylesheet" crossorigin href="/admin/static/index-vlBToOhT.css">
16
+ <script type="module" crossorigin src="/admin/static/index-8ahvKI6W.js"></script>
17
+ <link rel="stylesheet" crossorigin href="/admin/static/index-B8JOx1Ps.css">
18
18
  </head>
19
19
 
20
20
  <body>
@@ -21,6 +21,9 @@ class TranslateText(DataclassBase):
21
21
  def __init__(self, slug: str):
22
22
  self.slug = slug
23
23
 
24
+ def __hash__(self):
25
+ return hash(self.slug)
26
+
24
27
  @pydantic.model_serializer(mode='plain')
25
28
  def serialize_model(self, info: pydantic.SerializationInfo) -> str:
26
29
  ctx = info.context or {}
@@ -63,14 +66,14 @@ class LanguageManager(abc.ABC):
63
66
 
64
67
  builtin_locales_dir = resources.files("brilliance_admin").joinpath("locales")
65
68
  self.phrases.load_folder(builtin_locales_dir)
66
- logger.info('Language manager builtin dir loaded: %s', builtin_locales_dir)
69
+ logger.debug('Language manager builtin dir loaded: %s', builtin_locales_dir)
67
70
 
68
71
  if locales_dir:
69
72
  self.phrases.load_folder(locales_dir)
70
- logger.info('Language manager locales_dir loaded: %s', locales_dir)
73
+ logger.debug('Language manager locales_dir loaded: %s', locales_dir)
71
74
 
72
75
  langs = ', '.join(self.phrases.data.keys())
73
- logger.info('Language manager setup completed; languages: %s', langs)
76
+ logger.debug('Language manager setup completed; languages: %s', langs)
74
77
 
75
78
  def get_text(self, text, language) -> str:
76
79
  if not isinstance(text, TranslateText):
brilliance_admin/utils.py CHANGED
@@ -5,6 +5,7 @@ from typing import Any, Dict, Protocol
5
5
 
6
6
  import yaml
7
7
  from pydantic import TypeAdapter
8
+ from pydantic.fields import FieldInfo
8
9
  from pydantic_core import core_schema
9
10
 
10
11
 
@@ -37,13 +38,50 @@ class DeserializeAction:
37
38
  FILTERS = 3
38
39
 
39
40
 
41
+ class KwargsInitMixin:
42
+ """
43
+ Принимает только аргументы, объявленные в аннотациях.
44
+ Применяет default / default_factory из Field.
45
+ """
46
+
47
+ def __init__(self, **kwargs):
48
+ annotations = {}
49
+ for cls in type(self).__mro__:
50
+ annotations.update(getattr(cls, '__annotations__', {}))
51
+
52
+ allowed = set(annotations.keys())
53
+
54
+ for key, value in kwargs.items():
55
+ if key not in allowed:
56
+ raise AttributeError(
57
+ f'{type(self).__name__} has no field "{key}". '
58
+ f'Allowed fields: {sorted(allowed)}'
59
+ )
60
+ setattr(self, key, value)
61
+
62
+ self._apply_field_defaults()
63
+
64
+ def _apply_field_defaults(self):
65
+ for cls in type(self).__mro__:
66
+ for name, value in cls.__dict__.items():
67
+ if not isinstance(value, FieldInfo):
68
+ continue
69
+
70
+ # если в инстансе всё ещё FieldInfo — заменить
71
+ if getattr(self, name, None) is value:
72
+ if value.default_factory is not None:
73
+ setattr(self, name, value.default_factory())
74
+ elif value.default is not None:
75
+ setattr(self, name, value.default)
76
+
77
+
40
78
  class DataclassBase:
41
79
  def model_dump(self, *args, **kwargs) -> dict:
42
80
  adapter = TypeAdapter(type(self))
43
81
  return adapter.dump_python(self, *args, **kwargs)
44
82
 
45
- def to_dict(self, keep_none=True) -> dict:
46
- data = self.model_dump()
83
+ def to_dict(self, *args, keep_none=True, **kwargs) -> dict:
84
+ data = self.model_dump(*args, **kwargs)
47
85
  return {
48
86
  k: v for k, v in data.items()
49
87
  if v is not None and not keep_none
@@ -150,4 +188,4 @@ class YamlI18n:
150
188
  if isinstance(node, str):
151
189
  return node
152
190
 
153
- raise KeyError(f'translation key not found: {slug}')
191
+ return slug
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: brilliance-admin
3
+ Version: 0.44.12
4
+ Summary: Simple and lightweight data managment framework powered by FastAPI and Vue3 Vuetify all-in-one. Some call it heavenly in its brilliance.
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: asgiref>=3.11
10
+ Requires-Dist: fastapi>=0.115
11
+ Requires-Dist: jinja2>=3.1
12
+ Requires-Dist: PyYAML>=6.0
13
+ Provides-Extra: example
14
+ Requires-Dist: uvicorn>=0.34.0; extra == "example"
15
+ Requires-Dist: faker>=38.2.0; extra == "example"
16
+ Requires-Dist: pyjwt>=2.10.1; extra == "example"
17
+ Requires-Dist: structlog>=25.5.0; extra == "example"
18
+ Requires-Dist: rich>=14.2.0; extra == "example"
19
+ Requires-Dist: asyncpg>=0.31.0; extra == "example"
20
+ Requires-Dist: pydantic-settings>=2.12.0; extra == "example"
21
+ Requires-Dist: twine; extra == "example"
22
+ Provides-Extra: tests
23
+ Requires-Dist: pytest>=8.4.2; extra == "tests"
24
+ Requires-Dist: pytest-asyncio>=1.2.0; extra == "tests"
25
+ Requires-Dist: httpx>=0.28.1; extra == "tests"
26
+ Requires-Dist: pytest-mock>=3.15.1; extra == "tests"
27
+ Requires-Dist: sqlalchemy>=2.0.41; extra == "tests"
28
+ Requires-Dist: aiosqlite>=0.22.1; extra == "tests"
29
+ Requires-Dist: factory-boy>=3.3.3; extra == "tests"
30
+ Requires-Dist: pyjwt>=2.10.1; extra == "tests"
31
+ Provides-Extra: scalar
32
+ Requires-Dist: scalar-fastapi>=1.5.0; extra == "scalar"
33
+ Dynamic: license-file
34
+
35
+ <div align="center">
36
+ <img src="https://github.com/brilliance-admin/backend-python/blob/main/example/static/logo-outline.png?raw=true"
37
+ alt="Brilliance Admin"
38
+ width="600">
39
+
40
+ [![PyPI](https://img.shields.io/pypi/v/brilliance-admin)](https://pypi.org/project/brilliance-admin/)
41
+ [![CI](https://github.com/brilliance-admin/backend-python/actions/workflows/deploy.yml/badge.svg)](https://github.com/brilliance-admin/backend-python/actions)
42
+
43
+ Simple and lightweight data management framework powered by `FastAPI` and `Vue3` `Vuetify` all-in-one. \
44
+ Integrated with `SQLAlchemy`. Inspaired by Django Admin and DRF.\
45
+ _Some call it heavenly in its brilliance._
46
+
47
+ ### [Live Demo](https://brilliance-admin.com/) | [Demo Sources](https://github.com/brilliance-admin/backend-python/tree/main/example) | [Documentation](https://docs.brilliance-admin.com/)
48
+
49
+ <img src="https://github.com/brilliance-admin/backend-python/blob/main/screenshots/websitemockupgenerator.png?raw=true"
50
+ alt="Preview">
51
+
52
+ </div>
53
+
54
+ ### Brilliance Admin provides
55
+
56
+ A quick way to create a data management interface using:
57
+
58
+ - Admin page - endpoint with a prebuilt SPA [frontend Vue3 + Vuetify](https://github.com/brilliance-admin/frontend) <br>
59
+ This endpoint can be added to any ASGI compatable backend. For existing project or standalone admin app.
60
+ - API to fetch the UI JSON schema
61
+ - API methods for that UI to work with (to read and modify data)
62
+
63
+ <details open>
64
+ <summary><h2>Screenshots</h2></summary>
65
+ <div align="center"><img src="https://github.com/brilliance-admin/.github/blob/main/screenshots/login.png?raw=true"/></div>
66
+ <div align="center"><img src="https://github.com/brilliance-admin/.github/blob/main/screenshots/table.png?raw=true"/></div>
67
+ <div align="center"><img src="https://github.com/brilliance-admin/.github/blob/main/screenshots/charts.png?raw=true"/></div>
68
+ </details>
69
+
70
+ ## Key ideas
71
+
72
+ - **API Oriented** <br>
73
+ Data generation/updating API separated from rendering fontend with zero hardcode, this makes it possible to have a single frontend with multiple backend implementations in different languages and makes test coverage easier.
74
+ - **Rich visualization** <br>
75
+ Providing rich and convenient ways to display and manage data (tables, charts, etc) from any data source.
76
+ - **UI JSON Schema** <br>
77
+ Represents the data describing the structure of entire admin panel UI. <br>
78
+ You only need to specify what should be rendered. The frontend will display it and automatically request data from the backend for rendering or updates.
79
+ - **ORM** <br>
80
+ Automatic generation from ORM for schema UI frontend and backend methods for CRUD operations.
81
+ - **Minimal boilerplate** <br>
82
+ Focused on simplified, but rich configuration.
83
+
84
+ ## Features
85
+
86
+ * Tables with full CRUD support, including filtering, sorting, and pagination.
87
+ * Ability to define custom table actions with forms, response messages, and file downloads.
88
+ * Graphs via ChartJS
89
+ * Localization support
90
+ * Adapted for different screen sizes and mobile devices
91
+ * Auth via any account data source
92
+
93
+ **Integrations:**
94
+
95
+ * **SQLAlchemy** - schema autogeneration for tables + CRUD operations + authorization
96
+
97
+ **Planned:**
98
+
99
+ * Dashboard features
100
+ * Role-based access permissions system via interface
101
+ * Backend interface for storing and viewing action history in the admin interface
102
+ * Nested data support for creation and detail views (inline editing), nested CRUD workflows
103
+ * Django ORM integration
104
+ * Support for Oauth providers
105
+
106
+ ## Installation:
107
+ ``` shell
108
+ pip install brilliance-admin
109
+ ```
110
+
111
+ ## Usage example
112
+
113
+ You need to generate `AdminSchema` instance:
114
+ ``` python
115
+ from brilliance_admin import schema
116
+
117
+
118
+ class CategoryExample(schema.CategoryTable):
119
+ "Implementation of get_list and retrieve; update and create are optional"
120
+
121
+
122
+ admin_schema = schema.AdminSchema(
123
+ title='Admin Panel',
124
+ auth=YourAdminAuthentication(),
125
+ categories=[
126
+ schema.Category(
127
+ slug='example',
128
+ categories=[
129
+ CategoryExample(),
130
+ ]
131
+ ),
132
+ ],
133
+ )
134
+
135
+ admin_app = admin_schema.generate_app()
136
+
137
+ # Your FastAPI app (Any ASGI framework can be used)
138
+ app = FastAPI()
139
+ app.mount('/admin', admin_app)
140
+ ```
141
+
142
+ For more details, check out our [how-to-start documentation](https://docs.brilliance-admin.com/how-to-start/)
143
+
144
+ ## Comparison of Similar Projects
145
+
146
+ The project closest in concept is [React Admin](https://github.com/marmelab/react-admin). <br>
147
+ It is an SPA frontend that store the schema UI inside and works with separate API backend providers.
148
+
149
+ The key difference of Brilliance Admin is that its all-in-one. <br>
150
+ It is more focused on rapid setup for data management, without the need to work with frontend configuration, while it still available.
151
+
152
+ ## Comparison of Similar Python Projects
153
+
154
+ | Criterion | Brilliance Admin | Django Admin | FastAPI Admin | Starlette Admin | SQLAdmin |
155
+ |---------|------------------|--------------|---------------|-----------------|----------|
156
+ | Base framework | FastAPI | Django | FastAPI | Starlette | FastAPI |
157
+ | ASGI compatible | Yes | Partial | Yes | Yes | Yes |
158
+ | Rendering model | Prebuilt Vue 3 + Vuetify SPA + Jinja2 | Server-side Django templates | Server-side Jinja2 templates + Tabler UI | Server-side Jinja2 templates + Tabler UI | Server-side Jinja2 templates + Bootstrap |
159
+ | Frontend architecture | Separate frontend (SPA) | Classic server-rendered UI | Server-rendered UI with JS interactivity | Server-rendered UI with JS interactivity | Server-rendered UI |
160
+ | Data source | Any source + SQLAlchemy | Django ORM | Tortoise ORM | Any source + SQLAlchemy, MongoDB | SQLAlchemy |
161
+ | Multiple databases per model | Yes | Database routers | No (global engine) | Yes (session per ModelView) | No (single engine per Admin) |
162
+ | Schema generation | User-defined format | From Django models | From ORM models | User-defined format | From SQLAlchemy models |
163
+ | Async support | Yes | No | Yes | Yes | Yes |
164
+ | API-first approach | Yes | No | Partially | Partially | No |
165
+ | Built-in Localization | Yes | Yes | No | No | No |
@@ -1,51 +1,50 @@
1
1
  brilliance_admin/__init__.py,sha256=qxGzLVhFNm2FKL2lVt7bEFb6pTPqyXEQvUsDfyfv6SM,183
2
2
  brilliance_admin/auth.py,sha256=d57XRfLJIbOosLP1-0SCFkePPT8M5WhLcwxu4yW92RA,673
3
3
  brilliance_admin/docs.py,sha256=fKeJKuiCCi1jHRmNcmkuDD6_2di7bwc6-w8V1VCTu0s,1231
4
- brilliance_admin/exceptions.py,sha256=7_L3qVTwdLrzmDJjGv2yqCOVECP35wh0NyTvgjP7ETc,913
5
- brilliance_admin/translations.py,sha256=9IHS7ld0dghjIvHgko4ueRMoJ53VcblcFCEkgxQEG4A,3847
6
- brilliance_admin/utils.py,sha256=C5Rc7DUC9HnGIu4R9rdNGGtvZ3vrkWBNoDr-ijCPD3w,4593
4
+ brilliance_admin/exceptions.py,sha256=CJGvpo7EiaX3RVAEUDpwgH_Tuw8kY7tMy57pVcnBxak,947
5
+ brilliance_admin/translations.py,sha256=r5xy3Iara_7m2E7rgG8goPFDcKdPLUGAKIWIt6TdulA,3906
6
+ brilliance_admin/utils.py,sha256=M_0WANlb-1NW4aOA33MxOgoRx0GIVJ6w6PCGKAwmflk,5971
7
7
  brilliance_admin/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- brilliance_admin/api/routers.py,sha256=GXz-GFXRH5VbDH1r5O9LLnnmaMhbQq4RctzVEubHnsk,810
8
+ brilliance_admin/api/routers.py,sha256=S8IV7JxA1wLspYn3qxESMI3Qt6UhQK-Gy0PsZi8hcwQ,819
9
9
  brilliance_admin/api/utils.py,sha256=ezvHK49OlpCdT9fLB41Y1nswZDLdzC3zTcQtGtuTYUk,999
10
10
  brilliance_admin/api/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  brilliance_admin/api/views/auth.py,sha256=Qmd9KuFqCeNhQukYl0W62kcUYPMtgMekjkaTvz1AArM,1133
12
- brilliance_admin/api/views/autocomplete.py,sha256=oJbt-xD4sKnOyQIxT0mFqyRvPxFk9KUyJabKzLY888s,1508
13
- brilliance_admin/api/views/graphs.py,sha256=nn91zoFRZ0FvSkFJoTy7ZQEr8B2cUj43nd8OxPsSmTQ,1351
12
+ brilliance_admin/api/views/autocomplete.py,sha256=lpFDq46OCuiPhkUedv4y6GmJQVy3iX4P-di_-GMzabY,1516
13
+ brilliance_admin/api/views/dashboard.py,sha256=tLFdbKrePH-A91HNCN4F_FXdlRQ1tA9D2tGLvzDOoFM,1389
14
14
  brilliance_admin/api/views/index.py,sha256=DkMshrs5zmqpbbF9G2RhqeLGFKMrkrAmQ6nnba7aLl8,1400
15
15
  brilliance_admin/api/views/schema.py,sha256=MS9v9Qy3cRO9gGs4uW2PNRBS6Uw48sLRGRe49jnJ2yM,1019
16
16
  brilliance_admin/api/views/settings.py,sha256=2A9suZQONEtW9LkFban29Fe5ipQaaGT0CzpxnbuotJQ,1166
17
- brilliance_admin/api/views/table.py,sha256=29vLEwueHWBS8Dq6nBvujwy4CTs9v8ZcM1ErF9lYgIc,5932
17
+ brilliance_admin/api/views/table.py,sha256=dLo_ic9LPeAvkeelbkHZs9oC9g9ZlXPPMiQMszlwDIQ,5979
18
18
  brilliance_admin/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- brilliance_admin/integrations/sqlalchemy/__init__.py,sha256=tIegMU2eIW_zA-PdblCXCjenHAY4sGBUWKuz97ns4gE,275
20
- brilliance_admin/integrations/sqlalchemy/auth.py,sha256=99O7zmhcKWB3cWGTbMd3OSyIJ81Vcfz7byDK6oLinm8,4910
21
- brilliance_admin/integrations/sqlalchemy/autocomplete.py,sha256=baRroMwGg35uR5zt-aiQc2-ugX4hpwNf79R6bZH9UX0,1416
22
- brilliance_admin/integrations/sqlalchemy/fields.py,sha256=AayeFmwKA5JooiCok7wPLJNSDQCiy5p-iQjrr9HMMSw,9412
23
- brilliance_admin/integrations/sqlalchemy/fields_schema.py,sha256=fZu7I77POvcUHBVc7i0yKGZcAzDumVGMpsbX7TMByo4,10914
19
+ brilliance_admin/integrations/sqlalchemy/__init__.py,sha256=AmQHOvegS6_uaE4xYDHzRMdA9PyHn0cCt1s1IWc9G2U,318
20
+ brilliance_admin/integrations/sqlalchemy/auth.py,sha256=N9CjFfnXmKeNbJBxHMDlbPaTQPmYWlYTz_73PYBkaNk,4931
21
+ brilliance_admin/integrations/sqlalchemy/autocomplete.py,sha256=IKZwdjb5tM_pA5PBUhwYo4n_8nEfy_WcSu8Yu4ftarI,1572
22
+ brilliance_admin/integrations/sqlalchemy/fields.py,sha256=7D0E4-12rqraqHek2yNuHzRdO7hhxjp0edhJu-mTnWQ,9897
23
+ brilliance_admin/integrations/sqlalchemy/fields_schema.py,sha256=7WkOrt5dVvrsZppNZEZS8QaQPMSrfcWChwOaOFSRVOg,11419
24
24
  brilliance_admin/integrations/sqlalchemy/table/__init__.py,sha256=g_in2pLTi8UQnT5uNFA8mLW5mxlT84irQ7yVaP_OSS4,605
25
- brilliance_admin/integrations/sqlalchemy/table/base.py,sha256=nZzHk56NyhfTll9vX9j5zzA557mLdQ-u-GccTFXIFSs,4873
26
- brilliance_admin/integrations/sqlalchemy/table/create.py,sha256=z40PuKJYZ6goJdhsANVuEyTW1gh0PipCBdBSe5Ii5Tw,2693
27
- brilliance_admin/integrations/sqlalchemy/table/delete.py,sha256=Awv50IG7DQsYrHVlk4dtYBMwexmlO92xhMBgrgEDBgs,731
28
- brilliance_admin/integrations/sqlalchemy/table/list.py,sha256=--KVTLbuCr1fD79q0LXqlu6CuiYA3QhQj6rrdP7jR14,6692
29
- brilliance_admin/integrations/sqlalchemy/table/retrieve.py,sha256=b-Kyft1ecG2nAj8krTumyeY2LaONBGgBwyfLayL-ilw,2140
30
- brilliance_admin/integrations/sqlalchemy/table/update.py,sha256=AGEEbEsgKre784Dg4_kTKB6NfMwdAwNiMjm0ZKeg-LE,3543
31
- brilliance_admin/locales/en.yml,sha256=lCM58SoP4HExXFCTHpl8pYw82tH_8TvlrcCXZDXYb5c,1129
32
- brilliance_admin/locales/ru.yml,sha256=hXZwS71Z_aAyK9DB14pwmbtLI5xn6PvsnuLIZlEexbM,1661
33
- brilliance_admin/schema/__init__.py,sha256=X-izShvv84jkFU47WfpUwtvRh3NOv570iUB3NRNEIDU,248
34
- brilliance_admin/schema/admin_schema.py,sha256=fKVtaLkFkDF8ZkjIftackZ4rfCDcqLvci8PO6ZLycgc,6393
35
- brilliance_admin/schema/category.py,sha256=Y2DuDmSve_-Lnx3zOQlbVcslywJhcwV49z1bOhSKIGs,4105
36
- brilliance_admin/schema/group.py,sha256=Gdz3ME_EfQP_CFPou6OOJZWGWXKIYgB3zNX_oWsClwE,2240
37
- brilliance_admin/schema/graphs/__init__.py,sha256=qvmZv9QWdiutPtN5VYQLYbsjY2SOg8p_XRaz2rUlIxY,44
38
- brilliance_admin/schema/graphs/category_graphs.py,sha256=2nj_oiAoGXwGhc-gNVNFMNKDCkCdUWVn6u9CRCsb2DA,1513
25
+ brilliance_admin/integrations/sqlalchemy/table/base.py,sha256=-osqhTvqE7YcBxsAjqIUMWyYk1df04GIDmdvtieTXcg,4885
26
+ brilliance_admin/integrations/sqlalchemy/table/create.py,sha256=MJauwTe9armKyfGAdurVNrLKMAdC6tpHiEgAelgMCno,2871
27
+ brilliance_admin/integrations/sqlalchemy/table/delete.py,sha256=kcDEM7WDNu3lFdfdltN7nO2zQKHW3TQCYvGn1QcAyZc,755
28
+ brilliance_admin/integrations/sqlalchemy/table/list.py,sha256=Y76B5CFqQb2nfV0i0MrMrK3_kn9cyDvx10gEF8smrIA,8008
29
+ brilliance_admin/integrations/sqlalchemy/table/retrieve.py,sha256=EKwGH8Mz3Jd-uR43WibD3hFiZVGK_ra_5Cu5XeoEf6Y,3508
30
+ brilliance_admin/integrations/sqlalchemy/table/update.py,sha256=NxcYPqRZbm_BM-4QHzEbhgAhYSs3SXzYMMzKCalqahU,3800
31
+ brilliance_admin/locales/en.yml,sha256=YBjkq2Tm0KjXKOMgnIM5etO42Pkm8mDdQlvmpITwsA4,1453
32
+ brilliance_admin/locales/ru.yml,sha256=9MKJaJyx6dALP6CQkLQMJvgZdp8nwgnBxDsRZiYhlnc,2052
33
+ brilliance_admin/schema/__init__.py,sha256=H5UFO5dub_k5id8WdGRVy9G8RnqZuBSjw9XgPafj1VQ,293
34
+ brilliance_admin/schema/admin_schema.py,sha256=SGdNuYUKrwAc6SA4RYh8TgSrod5RuY7lpXKzmY1SHZ4,6812
35
+ brilliance_admin/schema/category.py,sha256=UTnUexbCHe9vfwi3ygX34YkLAH2ixfMWqe4H6xos0fI,6604
36
+ brilliance_admin/schema/dashboard/__init__.py,sha256=RxE5nNs5X3iWGXBOA518hFyJMSRLEcXr0PNb8HZrjxs,50
37
+ brilliance_admin/schema/dashboard/category_dashboard.py,sha256=rsEuWLpcfcrnzMeKIQCMFDYNF9H7LEfqEKlm63Sh7PE,2650
39
38
  brilliance_admin/schema/table/__init__.py,sha256=vuRw8HBuak2LaTZi2dNn5YOrJPalQps-O3Ht-d0AZV4,378
40
39
  brilliance_admin/schema/table/admin_action.py,sha256=0ymRL9DKkBK-AF6wKy7K9R4hkmblh55eHuZA_rjO1Lk,2018
41
- brilliance_admin/schema/table/category_table.py,sha256=BNdxspZ9Di6_bX0QQEGxgZLlddC7GIAWjBYha6bLtZo,6449
42
- brilliance_admin/schema/table/fields_schema.py,sha256=PvNHIYqDeLvM2qy32vCmLQ6VORl7bJQhSVcGUP1LIEw,8276
40
+ brilliance_admin/schema/table/category_table.py,sha256=AHR6fatZTdRKvUnks7DzQm0V0OwID-59khTxosEXoHE,6817
41
+ brilliance_admin/schema/table/fields_schema.py,sha256=E6U0oLREyCU-8HnwNB8Sf40al2bIzgs0-g5fOpqbQkw,8475
43
42
  brilliance_admin/schema/table/table_models.py,sha256=xidraifRYbXGkiVLn6dJ96dkOhW8-22ynE-fbiOjfAU,1018
44
43
  brilliance_admin/schema/table/fields/__init__.py,sha256=RW-sIFTAaSQo4mMR6iWtnefogWPjmg6KAsDwe9mKW1k,291
45
- brilliance_admin/schema/table/fields/base.py,sha256=gocPyNZvLFpv_3VXA8ptCcZKK8uVFlTiwzgiuOnN1FI,7791
44
+ brilliance_admin/schema/table/fields/base.py,sha256=rSVTsY1ph9ix33NiPslDjPjnwWvHwNtvt90FAByE4mQ,10243
46
45
  brilliance_admin/schema/table/fields/function_field.py,sha256=4fm9kS8zpBG5oqp9sA81NQDHiqvU0BQmpf-wjkTuuwM,1780
47
- brilliance_admin/static/index-D9axz5zK.js,sha256=KB_EHC9hoBK_LinkeMaE897Ws4Tl8xlJ8c_smbis_VE,3202111
48
- brilliance_admin/static/index-vlBToOhT.css,sha256=hoVCpcStTHdAVRm37k1umrNdXjOwIIveu9lxk4ocpEc,983028
46
+ brilliance_admin/static/index-8ahvKI6W.js,sha256=UVxVir-ZeOS8j83b7hAD-ZCanD7TO3vrNMgnclPFlP8,3209593
47
+ brilliance_admin/static/index-B8JOx1Ps.css,sha256=tvExD9r6ozGNJwjveAsdguzJ7Eab2iT-Z9coevdEtK0,984316
49
48
  brilliance_admin/static/materialdesignicons-webfont-CYDMK1kx.woff2,sha256=5S1g9kJnzaoIQitQurXUW9NeZisDua91F5zq4ArF_Is,385360
50
49
  brilliance_admin/static/materialdesignicons-webfont-CgCzGbLl.woff,sha256=SNPuxqtw3HoZCPm6LyCOClhxi57hbj9qvbXbT0Yfolg,561776
51
50
  brilliance_admin/static/materialdesignicons-webfont-D3kAzl71.ttf,sha256=vXJaejiTnltZkE4benJlkZ7OwlYWbs5p1RXCEAUWWQc,1243500
@@ -66,9 +65,9 @@ brilliance_admin/static/tinymce/plugins/accordion/css/accordion.css,sha256=u5UQk
66
65
  brilliance_admin/static/tinymce/plugins/codesample/css/prism.css,sha256=exAdMtHbvwW7-DEs567MX65Fq1aJQTfREP5pw8gW-AY,1736
67
66
  brilliance_admin/static/tinymce/plugins/customLink/plugin.js,sha256=illBNpnHDkBsLG6wo_jDPF6z7CGnO1MQWUoDwZKy6vQ,5589
68
67
  brilliance_admin/static/tinymce/plugins/customLink/css/link.css,sha256=gh5nvY8Z92hJfCEBPnIm4jIPCcKKbJnab-30oIfX7Hc,56
69
- brilliance_admin/templates/index.html,sha256=ZLJ_TKUvBDIo_hYfbW43ov0S_bFrzBF-283XP6BKtDI,1294
70
- brilliance_admin-0.43.1.dist-info/licenses/LICENSE,sha256=PjeDRXGbVLtKul5Xpfco_6CyB6bYGWVVPrO0oubquuM,727
71
- brilliance_admin-0.43.1.dist-info/METADATA,sha256=SgnEDyBA7AYrXHFP-T0K906C-rurqdqKHDkgr6Tl72s,7459
72
- brilliance_admin-0.43.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
73
- brilliance_admin-0.43.1.dist-info/top_level.txt,sha256=almFFSWrVYieI3i54hYL0fMUaeuIYiazS2Kx4wtK-ns,17
74
- brilliance_admin-0.43.1.dist-info/RECORD,,
68
+ brilliance_admin/templates/index.html,sha256=B7ExuuNN6qMvD5VHnezrHs_eYlwoLptGSHj96C9Q_EA,1294
69
+ brilliance_admin-0.44.12.dist-info/licenses/LICENSE,sha256=rgWE5Cxk53W0PhTOVmcQedABEWN1QMG-PRz3fz531sE,1074
70
+ brilliance_admin-0.44.12.dist-info/METADATA,sha256=Pt5porK8GT1bv2N3cknxbp31hzXXnarI7r3Dwvy3VVE,7393
71
+ brilliance_admin-0.44.12.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
72
+ brilliance_admin-0.44.12.dist-info/top_level.txt,sha256=almFFSWrVYieI3i54hYL0fMUaeuIYiazS2Kx4wtK-ns,17
73
+ brilliance_admin-0.44.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brilliance Admin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1 +0,0 @@
1
- from .category_graphs import CategoryGraphs
@@ -1,51 +0,0 @@
1
- from typing import Any, Dict, List
2
-
3
- from pydantic import BaseModel, Field
4
-
5
- from brilliance_admin.schema import Category
6
- from brilliance_admin.schema.category import GraphInfoSchemaData
7
- from brilliance_admin.schema.table.fields_schema import FieldsSchema
8
- from brilliance_admin.translations import LanguageContext
9
- from brilliance_admin.utils import SupportsStr
10
-
11
-
12
- class GraphData(BaseModel):
13
- search: str | None = None
14
- filters: Dict[str, Any] = Field(default_factory=dict)
15
-
16
-
17
- class ChartData(BaseModel):
18
- data: dict
19
- options: dict
20
- width: int | None = None
21
- height: int = 50
22
- type: str = 'line'
23
-
24
-
25
- class GraphsDataResult(BaseModel):
26
- charts: List[ChartData]
27
-
28
-
29
- class CategoryGraphs(Category):
30
- _type_slug: str = 'graphs'
31
-
32
- search_enabled: bool = False
33
- search_help: SupportsStr | None = None
34
-
35
- table_filters: FieldsSchema | None = None
36
-
37
- def generate_schema(self, user, language_context: LanguageContext) -> GraphInfoSchemaData:
38
- schema = super().generate_schema(user, language_context)
39
- graph = GraphInfoSchemaData(
40
- search_enabled=self.search_enabled,
41
- search_help=language_context.get_text(self.search_help),
42
- )
43
-
44
- if self.table_filters:
45
- graph.table_filters = self.table_filters.generate_schema(user, language_context)
46
-
47
- schema.graph_info = graph
48
- return schema
49
-
50
- async def get_data(self, data: GraphData, user) -> GraphsDataResult:
51
- raise NotImplementedError('get_data is not implemented')
@@ -1,67 +0,0 @@
1
- import abc
2
- from typing import Dict, List
3
-
4
- from pydantic.dataclasses import dataclass
5
-
6
- from brilliance_admin.auth import UserABC
7
- from brilliance_admin.schema.category import Category, CategorySchemaData
8
- from brilliance_admin.translations import LanguageContext
9
- from brilliance_admin.utils import DataclassBase, SupportsStr, get_logger
10
-
11
- logger = get_logger()
12
-
13
-
14
- @dataclass
15
- class GroupSchemaData(DataclassBase):
16
- title: str | None
17
- description: str | None
18
- icon: str | None
19
- categories: Dict[str, CategorySchemaData]
20
-
21
-
22
- @dataclass
23
- class Group(abc.ABC):
24
- categories: List[Category]
25
- slug: str
26
- title: SupportsStr | None = None
27
- description: SupportsStr | None = None
28
-
29
- # https://pictogrammers.com/library/mdi/
30
- icon: str | None = None
31
-
32
- def __post_init__(self):
33
- for category in self.categories:
34
- if not issubclass(category.__class__, Category):
35
- raise TypeError(f'Category "{category}" is not instance of Category subclass')
36
-
37
- def generate_schema(self, user: UserABC, language_context: LanguageContext) -> GroupSchemaData:
38
- result = GroupSchemaData(
39
- title=language_context.get_text(self.title) or self.slug,
40
- description=language_context.get_text(self.description),
41
- icon=self.icon,
42
- categories={},
43
- )
44
- if not self.categories:
45
- logger.warning('Group "%s" %s.categories is empty!', self.slug, type(self).__name__)
46
-
47
- for category in self.categories:
48
-
49
- if not category.slug:
50
- msg = f'Category {type(category).__name__}.slug is empty'
51
- raise AttributeError(msg)
52
-
53
- if category.slug in result.categories:
54
- exists = result.categories[category.slug]
55
- msg = f'Category {type(category).__name__}.slug "{self.slug}" already registered by "{exists.title}"'
56
- raise KeyError(msg)
57
-
58
- result.categories[category.slug] = category.generate_schema(user, language_context)
59
-
60
- return result
61
-
62
- def get_category(self, category_slug: str) -> Category | None:
63
- for category in self.categories:
64
- if category.slug == category_slug:
65
- return category
66
-
67
- return None