brilliance-admin 0.41.2__tar.gz → 0.42.0__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 (135) hide show
  1. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.isort.cfg +1 -1
  2. brilliance_admin-0.42.0/PKG-INFO +155 -0
  3. brilliance_admin-0.42.0/README.md +125 -0
  4. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/__init__.py +2 -2
  5. brilliance_admin-0.42.0/admin_panel/api/routers.py +18 -0
  6. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/utils.py +1 -1
  7. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/auth.py +6 -6
  8. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/autocomplete.py +9 -9
  9. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/graphs.py +8 -8
  10. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/index.py +4 -11
  11. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/schema.py +3 -3
  12. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/settings.py +5 -5
  13. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/table.py +23 -23
  14. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/auth.py +1 -1
  15. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/exceptions.py +4 -3
  16. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/auth.py +4 -4
  17. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/autocomplete.py +4 -4
  18. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/fields.py +10 -10
  19. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/fields_schema.py +6 -6
  20. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/table/base.py +4 -4
  21. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/table/create.py +7 -7
  22. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/table/delete.py +3 -3
  23. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/table/list.py +7 -7
  24. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/table/retrieve.py +6 -6
  25. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/table/update.py +6 -6
  26. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/admin_schema.py +30 -38
  27. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/category.py +8 -8
  28. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/graphs/category_graphs.py +9 -10
  29. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/group.py +10 -10
  30. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/admin_action.py +7 -8
  31. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/category_table.py +21 -21
  32. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/fields/base.py +21 -21
  33. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/fields/function_field.py +3 -3
  34. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/fields_schema.py +9 -9
  35. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/table_models.py +1 -1
  36. brilliance_admin-0.42.0/admin_panel/static/favicon.jpg +0 -0
  37. brilliance_admin-0.41.2/brilliance_admin/static/index-D9axz5zK.js → brilliance_admin-0.42.0/admin_panel/static/index-BeniOHDv.js +24 -24
  38. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/templates/index.html +1 -1
  39. brilliance_admin-0.42.0/admin_panel/translations.py +145 -0
  40. brilliance_admin-0.42.0/admin_panel/utils.py +50 -0
  41. brilliance_admin-0.42.0/brilliance_admin.egg-info/PKG-INFO +155 -0
  42. brilliance_admin-0.42.0/brilliance_admin.egg-info/SOURCES.txt +113 -0
  43. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/brilliance_admin.egg-info/requires.txt +3 -4
  44. brilliance_admin-0.42.0/brilliance_admin.egg-info/top_level.txt +1 -0
  45. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/main.py +18 -17
  46. brilliance_admin-0.42.0/example/phrases.py +112 -0
  47. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/currency.py +3 -4
  48. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/graphs.py +3 -3
  49. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/merchant.py +2 -2
  50. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/models.py +1 -1
  51. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/payments.py +10 -10
  52. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/terminal.py +2 -2
  53. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/users.py +2 -2
  54. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/pyproject.toml +7 -8
  55. brilliance_admin-0.42.0/tests/conftest.py +12 -0
  56. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/tests/test_action.py +1 -1
  57. brilliance_admin-0.41.2/tests/test_settings.py → brilliance_admin-0.42.0/tests/test_index_context_data.py +0 -16
  58. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/tests/test_payments_fields_schema.py +5 -4
  59. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/tests/test_sqlalcmeny_auth.py +4 -4
  60. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/tests/test_sqlalcmeny_crud.py +23 -15
  61. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/tests/test_sqlalcmeny_filters.py +21 -15
  62. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/tests/test_sqlalcmeny_schema.py +6 -4
  63. brilliance_admin-0.42.0/tests/test_translations.py +90 -0
  64. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/uv.lock +119 -232
  65. brilliance_admin-0.41.2/PKG-INFO +0 -214
  66. brilliance_admin-0.41.2/README.md +0 -183
  67. brilliance_admin-0.41.2/brilliance_admin/api/routers.py +0 -18
  68. brilliance_admin-0.41.2/brilliance_admin/locales/en.yml +0 -22
  69. brilliance_admin-0.41.2/brilliance_admin/locales/ru.yml +0 -22
  70. brilliance_admin-0.41.2/brilliance_admin/translations.py +0 -115
  71. brilliance_admin-0.41.2/brilliance_admin/utils.py +0 -153
  72. brilliance_admin-0.41.2/brilliance_admin.egg-info/PKG-INFO +0 -214
  73. brilliance_admin-0.41.2/brilliance_admin.egg-info/SOURCES.txt +0 -121
  74. brilliance_admin-0.41.2/brilliance_admin.egg-info/top_level.txt +0 -1
  75. brilliance_admin-0.41.2/example/locales/en.yml +0 -52
  76. brilliance_admin-0.41.2/example/locales/ru.yml +0 -51
  77. brilliance_admin-0.41.2/screenshots/PC-graphs.jpeg +0 -0
  78. brilliance_admin-0.41.2/screenshots/PC-table.jpeg +0 -0
  79. brilliance_admin-0.41.2/screenshots/iPad-edit.jpeg +0 -0
  80. brilliance_admin-0.41.2/screenshots/iPhone 15-edit.jpeg +0 -0
  81. brilliance_admin-0.41.2/screenshots/iPhone 15-login.jpeg +0 -0
  82. brilliance_admin-0.41.2/screenshots/websitemockupgenerator.png +0 -0
  83. brilliance_admin-0.41.2/tests/conftest.py +0 -26
  84. brilliance_admin-0.41.2/tests/test_translations.py +0 -40
  85. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.configs/docker/Dockerfile +0 -0
  86. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.configs/docker/docker-compose.yml +0 -0
  87. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.configs/nginx/example.conf +0 -0
  88. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.env +0 -0
  89. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.github/workflows/certbot.yml +0 -0
  90. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.github/workflows/deploy.yml +0 -0
  91. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.github/workflows/install-docker.yml +0 -0
  92. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.gitignore +0 -0
  93. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/.python-version +0 -0
  94. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/LICENSE +0 -0
  95. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/__init__.py +0 -0
  96. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/api/views/__init__.py +0 -0
  97. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/docs.py +0 -0
  98. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/__init__.py +0 -0
  99. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/__init__.py +0 -0
  100. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/integrations/sqlalchemy/table/__init__.py +0 -0
  101. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/__init__.py +0 -0
  102. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/graphs/__init__.py +0 -0
  103. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/__init__.py +0 -0
  104. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/schema/table/fields/__init__.py +0 -0
  105. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/index-vlBToOhT.css +0 -0
  106. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
  107. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
  108. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
  109. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
  110. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/dark-first/content.min.css +0 -0
  111. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/dark-first/skin.min.css +0 -0
  112. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/dark-slim/content.min.css +0 -0
  113. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/dark-slim/skin.min.css +0 -0
  114. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/img/example.png +0 -0
  115. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/img/tinymce.woff2 +0 -0
  116. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/lightgray/content.min.css +0 -0
  117. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
  118. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/lightgray/skin.min.css +0 -0
  119. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/plugins/accordion/css/accordion.css +0 -0
  120. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/plugins/accordion/plugin.js +0 -0
  121. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/plugins/codesample/css/prism.css +0 -0
  122. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/plugins/customLink/css/link.css +0 -0
  123. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/plugins/customLink/plugin.js +0 -0
  124. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/tinymce/tinymce.min.js +0 -0
  125. {brilliance_admin-0.41.2/brilliance_admin → brilliance_admin-0.42.0/admin_panel}/static/vanilla-picker-B6E6ObS_.js +0 -0
  126. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/brilliance_admin.egg-info/dependency_links.txt +0 -0
  127. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/README.md +0 -0
  128. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/__init__.py +0 -0
  129. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sections/__init__.py +0 -0
  130. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/sqlite.py +0 -0
  131. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/static/logo-outline.png +0 -0
  132. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/static/logo.png +0 -0
  133. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/example/utils.py +0 -0
  134. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/setup.cfg +0 -0
  135. {brilliance_admin-0.41.2 → brilliance_admin-0.42.0}/tests/__init__.py +0 -0
@@ -3,5 +3,5 @@ line_length=120
3
3
  py_version=39
4
4
  multi_line_output=4
5
5
  known_third_party=fastapi
6
- known_first_party=app,tests,brilliance_admin
6
+ known_first_party=app,tests
7
7
  virtual_env=py3
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: brilliance-admin
3
+ Version: 0.42.0
4
+ Summary: General-purpose admin panel framework powered by FastAPI. Some call it heavenly in its brilliance.
5
+ License-Expression: AGPL-3.0
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: asgiref>=3.11.0
10
+ Requires-Dist: fastapi>=0.115.8
11
+ Requires-Dist: jinja2>=3.1.6
12
+ Provides-Extra: example
13
+ Requires-Dist: uvicorn>=0.34.0; extra == "example"
14
+ Requires-Dist: faker>=38.2.0; extra == "example"
15
+ Requires-Dist: pyjwt>=2.10.1; extra == "example"
16
+ Requires-Dist: structlog>=25.5.0; extra == "example"
17
+ Requires-Dist: rich>=14.2.0; extra == "example"
18
+ Provides-Extra: tests
19
+ Requires-Dist: pytest>=8.4.2; extra == "tests"
20
+ Requires-Dist: pytest-asyncio>=1.2.0; extra == "tests"
21
+ Requires-Dist: httpx>=0.28.1; extra == "tests"
22
+ Requires-Dist: pytest-mock>=3.15.1; extra == "tests"
23
+ Requires-Dist: sqlalchemy>=2.0.41; extra == "tests"
24
+ Requires-Dist: aiosqlite>=0.22.1; extra == "tests"
25
+ Requires-Dist: factory-boy>=3.3.3; extra == "tests"
26
+ Requires-Dist: pyjwt>=2.10.1; extra == "tests"
27
+ Provides-Extra: scalar
28
+ Requires-Dist: scalar-fastapi>=1.5.0; extra == "scalar"
29
+ Dynamic: license-file
30
+
31
+ <div align="center">
32
+ <img src="https://github.com/brilliance-admin/backend-python/blob/main/example/static/logo-outline.png?raw=true"
33
+ alt="Brilliance Admin"
34
+ width="600">
35
+ </div>
36
+
37
+ <div align="center">
38
+
39
+ [![PyPI](https://img.shields.io/pypi/v/brilliance-admin)](https://pypi.org/project/brilliance-admin/)
40
+ [![License](https://img.shields.io/pypi/l/brilliance-admin)](https://github.com/brilliance-admin/backend-python/blob/main/LICENSE)
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
+ </div>
44
+
45
+ General-purpose admin panel framework powered by FastAPI. Some call it heavenly in its brilliance.
46
+
47
+ - Serves a prebuilt SPA frontend as static files
48
+ - Generates schemas for frontend sections on the backend
49
+ - Provides a backend-driven API for admin interfaces
50
+ - Designed for fast data management and data viewing from any sources
51
+ - Inspired by Django Admin and Django REST Framework
52
+ - Focused on minimal boilerplate and simplified backend-controlled configuration
53
+
54
+ ### [Live Demo](https://brilliance-admin.com/) | [Example App](https://github.com/brilliance-admin/backend-python/tree/main/example) | Documentation (todo)
55
+
56
+
57
+ ### Features:
58
+
59
+ * Tables with full CRUD support, including filtering, sorting, and pagination.
60
+ * Ability to define custom table actions with forms, response messages, and file uploads.
61
+ * SQLAlchemy integration with automatic field generation from models.
62
+ * Authorization via any account data source.
63
+ * Localization support with language selection in the interface.
64
+
65
+ ## How to use it
66
+
67
+ Installation:
68
+ ``` shell
69
+ pip install brilliance-admin
70
+ ```
71
+
72
+ You need to generate `AdminSchema` instance:
73
+ ``` python
74
+ from admin_panel import schema
75
+
76
+ admin_schema = schema.AdminSchema(
77
+ title='Admin Panel',
78
+ auth=YourAdminAuthentication(),
79
+ groups=[
80
+ schema.Group(
81
+ slug='example',
82
+ title='Example',
83
+ icon='mdi-star',
84
+ categories=[
85
+ CategoryExample(),
86
+ ]
87
+ ),
88
+ ],
89
+ )
90
+
91
+ admin_app = admin_schema.generate_app()
92
+
93
+ # Your FastAPI app
94
+ app = FastAPI()
95
+ app.mount('/admin', admin_app)
96
+ ```
97
+
98
+ ### SQLAlchemy integration
99
+ Brilliance Admin supports automatic schema generation from SQLAlchemy and provides a ready-made CRUD implementation for tables.
100
+
101
+ ``` python
102
+ from admin_panel import sqlalchemy
103
+ from admin_panel.translations import TranslateText as _
104
+
105
+ from your_project.models import Terminal
106
+
107
+
108
+ class TerminalAdmin(sqlalchemy.SQLAlchemyAdmin):
109
+ db_async_session = async_sessionmaker
110
+ model = Terminal
111
+ title = _('terminals')
112
+ icon = 'mdi-console-network-outline'
113
+
114
+ ordering_fields = ['id']
115
+ search_fields = ['id', 'title']
116
+
117
+ table_schema = sqlalchemy.SQLAlchemyFieldsSchema(
118
+ model=Terminal,
119
+ list_display=['id', 'merchant_id'],
120
+ )
121
+ table_filters = sqlalchemy.SQLAlchemyFieldsSchema(
122
+ model=Terminal,
123
+ fields=['id', 'created_at'],
124
+ created_at=schema.DateTimeField(range=True),
125
+ )
126
+
127
+
128
+ category = TerminalAdmin()
129
+ ```
130
+
131
+ Now, the `TerminalAdmin` instance can be passed to `categories`.
132
+
133
+ ### Can be used both via inheritance and instancing
134
+
135
+ For `SQLAlchemyFieldsSchema`
136
+
137
+ ``` python
138
+ class TerminalTableSchema(sqlalchemy.SQLAlchemyFieldsSchema):
139
+ model = Terminal
140
+ fields = ['id', 'created_at']
141
+
142
+ created_at=schema.DateTimeField(range=True)
143
+
144
+
145
+ class TerminalAdmin(sqlalchemy.SQLAlchemyAdmin):
146
+ table_schema = TerminalTableSchema()
147
+ ```
148
+
149
+ And `SQLAlchemyAdmin` category schema itself
150
+
151
+ > If `table_schema` is not specified, it will be generated automatically and will include all discovered fields and relationships of the table in the output.
152
+
153
+ ``` python
154
+ category = sqlalchemy.SQLAlchemyAdmin(db_async_session=async_sessionmaker, model=Terminal)
155
+ ```
@@ -0,0 +1,125 @@
1
+ <div align="center">
2
+ <img src="https://github.com/brilliance-admin/backend-python/blob/main/example/static/logo-outline.png?raw=true"
3
+ alt="Brilliance Admin"
4
+ width="600">
5
+ </div>
6
+
7
+ <div align="center">
8
+
9
+ [![PyPI](https://img.shields.io/pypi/v/brilliance-admin)](https://pypi.org/project/brilliance-admin/)
10
+ [![License](https://img.shields.io/pypi/l/brilliance-admin)](https://github.com/brilliance-admin/backend-python/blob/main/LICENSE)
11
+ [![CI](https://github.com/brilliance-admin/backend-python/actions/workflows/deploy.yml/badge.svg)](https://github.com/brilliance-admin/backend-python/actions)
12
+
13
+ </div>
14
+
15
+ General-purpose admin panel framework powered by FastAPI. Some call it heavenly in its brilliance.
16
+
17
+ - Serves a prebuilt SPA frontend as static files
18
+ - Generates schemas for frontend sections on the backend
19
+ - Provides a backend-driven API for admin interfaces
20
+ - Designed for fast data management and data viewing from any sources
21
+ - Inspired by Django Admin and Django REST Framework
22
+ - Focused on minimal boilerplate and simplified backend-controlled configuration
23
+
24
+ ### [Live Demo](https://brilliance-admin.com/) | [Example App](https://github.com/brilliance-admin/backend-python/tree/main/example) | Documentation (todo)
25
+
26
+
27
+ ### Features:
28
+
29
+ * Tables with full CRUD support, including filtering, sorting, and pagination.
30
+ * Ability to define custom table actions with forms, response messages, and file uploads.
31
+ * SQLAlchemy integration with automatic field generation from models.
32
+ * Authorization via any account data source.
33
+ * Localization support with language selection in the interface.
34
+
35
+ ## How to use it
36
+
37
+ Installation:
38
+ ``` shell
39
+ pip install brilliance-admin
40
+ ```
41
+
42
+ You need to generate `AdminSchema` instance:
43
+ ``` python
44
+ from admin_panel import schema
45
+
46
+ admin_schema = schema.AdminSchema(
47
+ title='Admin Panel',
48
+ auth=YourAdminAuthentication(),
49
+ groups=[
50
+ schema.Group(
51
+ slug='example',
52
+ title='Example',
53
+ icon='mdi-star',
54
+ categories=[
55
+ CategoryExample(),
56
+ ]
57
+ ),
58
+ ],
59
+ )
60
+
61
+ admin_app = admin_schema.generate_app()
62
+
63
+ # Your FastAPI app
64
+ app = FastAPI()
65
+ app.mount('/admin', admin_app)
66
+ ```
67
+
68
+ ### SQLAlchemy integration
69
+ Brilliance Admin supports automatic schema generation from SQLAlchemy and provides a ready-made CRUD implementation for tables.
70
+
71
+ ``` python
72
+ from admin_panel import sqlalchemy
73
+ from admin_panel.translations import TranslateText as _
74
+
75
+ from your_project.models import Terminal
76
+
77
+
78
+ class TerminalAdmin(sqlalchemy.SQLAlchemyAdmin):
79
+ db_async_session = async_sessionmaker
80
+ model = Terminal
81
+ title = _('terminals')
82
+ icon = 'mdi-console-network-outline'
83
+
84
+ ordering_fields = ['id']
85
+ search_fields = ['id', 'title']
86
+
87
+ table_schema = sqlalchemy.SQLAlchemyFieldsSchema(
88
+ model=Terminal,
89
+ list_display=['id', 'merchant_id'],
90
+ )
91
+ table_filters = sqlalchemy.SQLAlchemyFieldsSchema(
92
+ model=Terminal,
93
+ fields=['id', 'created_at'],
94
+ created_at=schema.DateTimeField(range=True),
95
+ )
96
+
97
+
98
+ category = TerminalAdmin()
99
+ ```
100
+
101
+ Now, the `TerminalAdmin` instance can be passed to `categories`.
102
+
103
+ ### Can be used both via inheritance and instancing
104
+
105
+ For `SQLAlchemyFieldsSchema`
106
+
107
+ ``` python
108
+ class TerminalTableSchema(sqlalchemy.SQLAlchemyFieldsSchema):
109
+ model = Terminal
110
+ fields = ['id', 'created_at']
111
+
112
+ created_at=schema.DateTimeField(range=True)
113
+
114
+
115
+ class TerminalAdmin(sqlalchemy.SQLAlchemyAdmin):
116
+ table_schema = TerminalTableSchema()
117
+ ```
118
+
119
+ And `SQLAlchemyAdmin` category schema itself
120
+
121
+ > If `table_schema` is not specified, it will be generated automatically and will include all discovered fields and relationships of the table in the output.
122
+
123
+ ``` python
124
+ category = sqlalchemy.SQLAlchemyAdmin(db_async_session=async_sessionmaker, model=Terminal)
125
+ ```
@@ -1,4 +1,4 @@
1
1
  # pylint: disable=wildcard-import, unused-wildcard-import, unused-import
2
2
  # flake8: noqa: F405
3
- from brilliance_admin.integrations import sqlalchemy
4
- from brilliance_admin import schema
3
+ from admin_panel.integrations import sqlalchemy
4
+ from admin_panel import schema
@@ -0,0 +1,18 @@
1
+ from fastapi import APIRouter
2
+
3
+ from .views.schema import router as schema_router
4
+ from .views.table import router as schema_table
5
+ from .views.auth import router as schema_auth
6
+ from .views.autocomplete import router as schema_autocomplete
7
+ from .views.graphs import router as schema_graphs
8
+ from .views.settings import router as schema_settings
9
+ from .views.index import router as schema_index
10
+
11
+ admin_panel_router = APIRouter()
12
+ admin_panel_router.include_router(schema_router)
13
+ admin_panel_router.include_router(schema_table)
14
+ admin_panel_router.include_router(schema_auth)
15
+ admin_panel_router.include_router(schema_autocomplete)
16
+ admin_panel_router.include_router(schema_graphs)
17
+ admin_panel_router.include_router(schema_settings)
18
+ admin_panel_router.include_router(schema_index)
@@ -1,6 +1,6 @@
1
1
  from fastapi import HTTPException
2
2
 
3
- from brilliance_admin.auth import AdminAuthentication
3
+ from admin_panel.auth import AdminAuthentication
4
4
 
5
5
 
6
6
  async def get_user(request):
@@ -1,10 +1,10 @@
1
1
  from fastapi import APIRouter, Request
2
2
  from fastapi.responses import JSONResponse
3
3
 
4
- from brilliance_admin.auth import AdminAuthentication, AuthData, AuthResult
5
- from brilliance_admin.exceptions import AdminAPIException, APIError
6
- from brilliance_admin.schema.admin_schema import AdminSchema
7
- from brilliance_admin.translations import LanguageContext
4
+ from admin_panel.auth import AdminAuthentication, AuthData, AuthResult
5
+ from admin_panel.exceptions import AdminAPIException, APIError
6
+ from admin_panel.schema.admin_schema import AdminSchema
7
+ from admin_panel.translations import LanguageManager
8
8
 
9
9
  router = APIRouter(prefix="/auth", tags=["Auth"])
10
10
 
@@ -17,8 +17,8 @@ async def login(request: Request, auth_data: AuthData) -> AuthResult:
17
17
  schema: AdminSchema = request.app.state.schema
18
18
 
19
19
  language_slug = request.headers.get('Accept-Language')
20
- language_context: LanguageContext = schema.get_language_context(language_slug)
21
- context = {'language_context': language_context}
20
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
21
+ context = {'language_manager': language_manager}
22
22
 
23
23
  auth: AdminAuthentication = schema.auth
24
24
  try:
@@ -1,12 +1,12 @@
1
1
  from fastapi import APIRouter, Request
2
2
  from fastapi.responses import JSONResponse
3
3
 
4
- from brilliance_admin.api.utils import get_category
5
- from brilliance_admin.exceptions import AdminAPIException
6
- from brilliance_admin.schema.admin_schema import AdminSchema
7
- from brilliance_admin.schema.table.table_models import AutocompleteData, AutocompleteResult
8
- from brilliance_admin.translations import LanguageContext
9
- from brilliance_admin.utils import get_logger
4
+ from admin_panel.api.utils import get_category
5
+ from admin_panel.exceptions import AdminAPIException
6
+ from admin_panel.schema.admin_schema import AdminSchema
7
+ from admin_panel.schema.table.table_models import AutocompleteData, AutocompleteResult
8
+ from admin_panel.translations import LanguageManager
9
+ from admin_panel.utils import get_logger
10
10
 
11
11
  router = APIRouter(prefix="/autocomplete", tags=["Autocomplete"])
12
12
 
@@ -19,11 +19,11 @@ async def autocomplete(request: Request, group: str, category: str, data: Autoco
19
19
  schema_category, user = await get_category(request, group, category)
20
20
 
21
21
  language_slug = request.headers.get('Accept-Language')
22
- language_context: LanguageContext = schema.get_language_context(language_slug)
23
- context = {'language_context': language_context}
22
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
23
+ context = {'language_manager': language_manager}
24
24
 
25
25
  try:
26
- result: AutocompleteResult = await schema_category.autocomplete(data, user, language_context)
26
+ result: AutocompleteResult = await schema_category.autocomplete(data, user, language_manager)
27
27
  except AdminAPIException as e:
28
28
  return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
29
29
  except Exception as e:
@@ -1,12 +1,12 @@
1
1
  from fastapi import APIRouter, Request
2
2
  from fastapi.responses import JSONResponse
3
3
 
4
- from brilliance_admin.api.utils import get_category
5
- from brilliance_admin.exceptions import AdminAPIException
6
- from brilliance_admin.schema.admin_schema import AdminSchema
7
- from brilliance_admin.schema.graphs.category_graphs import CategoryGraphs, GraphData, GraphsDataResult
8
- from brilliance_admin.translations import LanguageContext
9
- from brilliance_admin.utils import get_logger
4
+ from admin_panel.api.utils import get_category
5
+ from admin_panel.exceptions import AdminAPIException
6
+ from admin_panel.schema.admin_schema import AdminSchema
7
+ from admin_panel.schema.graphs.category_graphs import CategoryGraphs, GraphData, GraphsDataResult
8
+ from admin_panel.translations import LanguageManager
9
+ from admin_panel.utils import get_logger
10
10
 
11
11
  router = APIRouter(prefix="/graph", tags=["Category - Graph"])
12
12
 
@@ -21,8 +21,8 @@ async def graph_data(request: Request, group: str, category: str, data: GraphDat
21
21
  result: GraphsDataResult = await schema_category.get_data(data, user)
22
22
 
23
23
  language_slug = request.headers.get('Accept-Language')
24
- language_context: LanguageContext = schema.get_language_context(language_slug)
25
- context = {'language_context': language_context}
24
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
25
+ context = {'language_manager': language_manager}
26
26
 
27
27
  try:
28
28
  return JSONResponse(result.model_dump(mode='json', context=context))
@@ -1,17 +1,15 @@
1
- from pathlib import PurePosixPath
2
-
3
1
  from fastapi import APIRouter, HTTPException, Request
4
2
  from fastapi.responses import HTMLResponse
5
3
  from fastapi.templating import Jinja2Templates
6
4
  from jinja2 import Environment, PackageLoader, select_autoescape
7
5
 
8
- from brilliance_admin.schema import AdminSchema
6
+ from admin_panel.schema import AdminSchema
9
7
 
10
8
  router = APIRouter()
11
9
 
12
10
  templates = Jinja2Templates(
13
11
  env=Environment(
14
- loader=PackageLoader("brilliance_admin", "templates"),
12
+ loader=PackageLoader("admin_panel", "templates"),
15
13
  autoescape=select_autoescape(["html", "xml"]),
16
14
  )
17
15
  )
@@ -27,13 +25,8 @@ async def admin_index(request: Request, rest_of_path: str):
27
25
  The request responds with a pre-rendered SPA served as an HTML page.
28
26
  '''
29
27
 
30
- path = PurePosixPath('/' + rest_of_path)
31
-
32
- if '..' in path.parts:
33
- raise HTTPException(status_code=404)
34
-
35
- path_str = str(path)
36
- if path_str in EXACT_BLOCK or path_str.startswith(PREFIX_BLOCK):
28
+ path = "/" + rest_of_path
29
+ if path in EXACT_BLOCK or path.startswith(PREFIX_BLOCK):
37
30
  raise HTTPException(status_code=404)
38
31
 
39
32
  schema: AdminSchema = request.app.state.schema
@@ -1,9 +1,9 @@
1
1
  from fastapi import APIRouter, Request
2
2
  from fastapi.responses import JSONResponse
3
3
 
4
- from brilliance_admin.auth import AdminAuthentication
5
- from brilliance_admin.exceptions import AdminAPIException, APIError
6
- from brilliance_admin.schema import AdminSchema, AdminSchemaData
4
+ from admin_panel.auth import AdminAuthentication
5
+ from admin_panel.exceptions import AdminAPIException, APIError
6
+ from admin_panel.schema import AdminSchema, AdminSchemaData
7
7
 
8
8
  router = APIRouter(prefix="/schema", tags=["Main admin schema"])
9
9
 
@@ -1,9 +1,9 @@
1
1
  from fastapi import APIRouter, Request
2
2
  from fastapi.responses import JSONResponse
3
3
 
4
- from brilliance_admin.exceptions import AdminAPIException, APIError
5
- from brilliance_admin.schema.admin_schema import AdminSchema, AdminSettingsData
6
- from brilliance_admin.translations import LanguageContext
4
+ from admin_panel.exceptions import AdminAPIException, APIError
5
+ from admin_panel.schema.admin_schema import AdminSchema, AdminSettingsData
6
+ from admin_panel.translations import LanguageManager
7
7
 
8
8
  router = APIRouter(tags=["Settings"])
9
9
 
@@ -19,8 +19,8 @@ async def get_settings(request: Request) -> AdminSettingsData:
19
19
  schema: AdminSchema = request.app.state.schema
20
20
 
21
21
  language_slug = request.headers.get('Accept-Language')
22
- language_context: LanguageContext = schema.get_language_context(language_slug)
23
- context = {'language_context': language_context}
22
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
23
+ context = {'language_manager': language_manager}
24
24
 
25
25
  try:
26
26
  admin_settings = await schema.get_settings(request)
@@ -3,14 +3,14 @@ from typing import Any
3
3
  from fastapi import APIRouter, HTTPException, Request
4
4
  from fastapi.responses import JSONResponse
5
5
 
6
- from brilliance_admin.api.utils import get_category
7
- from brilliance_admin.exceptions import AdminAPIException, APIError
8
- from brilliance_admin.schema import AdminSchema
9
- from brilliance_admin.schema.table.admin_action import ActionData, ActionResult
10
- from brilliance_admin.schema.table.category_table import CategoryTable
11
- from brilliance_admin.schema.table.table_models import CreateResult, ListData, RetrieveResult, TableListResult, UpdateResult
12
- from brilliance_admin.translations import LanguageContext
13
- from brilliance_admin.utils import get_logger
6
+ from admin_panel.api.utils import get_category
7
+ from admin_panel.exceptions import AdminAPIException, APIError
8
+ from admin_panel.schema import AdminSchema
9
+ from admin_panel.schema.table.admin_action import ActionData, ActionResult
10
+ from admin_panel.schema.table.category_table import CategoryTable
11
+ from admin_panel.schema.table.table_models import CreateResult, ListData, RetrieveResult, TableListResult, UpdateResult
12
+ from admin_panel.translations import LanguageManager
13
+ from admin_panel.utils import get_logger
14
14
 
15
15
  router = APIRouter(prefix="/table", tags=["Category - Table"])
16
16
 
@@ -25,11 +25,11 @@ async def table_list(request: Request, group: str, category: str, list_data: Lis
25
25
  schema_category, user = await get_category(request, group, category, check_type=CategoryTable)
26
26
 
27
27
  language_slug = request.headers.get('Accept-Language')
28
- language_context: LanguageContext = schema.get_language_context(language_slug)
29
- context = {'language_context': language_context}
28
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
29
+ context = {'language_manager': language_manager}
30
30
 
31
31
  try:
32
- result: TableListResult = await schema_category.get_list(list_data, user, language_context)
32
+ result: TableListResult = await schema_category.get_list(list_data, user, language_manager)
33
33
  except AdminAPIException as e:
34
34
  return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
35
35
 
@@ -49,11 +49,11 @@ async def table_retrieve(request: Request, group: str, category: str, pk: Any) -
49
49
  raise HTTPException(status_code=404, detail=f"Category {group}.{category} is not allowed for retrive")
50
50
 
51
51
  language_slug = request.headers.get('Accept-Language')
52
- language_context: LanguageContext = schema.get_language_context(language_slug)
53
- context = {'language_context': language_context}
52
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
53
+ context = {'language_manager': language_manager}
54
54
 
55
55
  try:
56
- result: RetrieveResult = await schema_category.retrieve(pk, user, language_context)
56
+ result: RetrieveResult = await schema_category.retrieve(pk, user, language_manager)
57
57
  except AdminAPIException as e:
58
58
  return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
59
59
 
@@ -72,11 +72,11 @@ async def table_create(request: Request, group: str, category: str) -> CreateRes
72
72
  raise HTTPException(status_code=404, detail=f"Category {group}.{category} is not allowed for create")
73
73
 
74
74
  language_slug = request.headers.get('Accept-Language')
75
- language_context: LanguageContext = schema.get_language_context(language_slug)
76
- context = {'language_context': language_context}
75
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
76
+ context = {'language_manager': language_manager}
77
77
 
78
78
  try:
79
- result: CreateResult = await schema_category.create(await request.json(), user, language_context)
79
+ result: CreateResult = await schema_category.create(await request.json(), user, language_manager)
80
80
  except AdminAPIException as e:
81
81
  return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
82
82
 
@@ -95,11 +95,11 @@ async def table_update(request: Request, group: str, category: str, pk: Any) ->
95
95
  raise HTTPException(status_code=404, detail=f"Category {group}.{category} is not allowed for update")
96
96
 
97
97
  language_slug = request.headers.get('Accept-Language')
98
- language_context: LanguageContext = schema.get_language_context(language_slug)
99
- context = {'language_context': language_context}
98
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
99
+ context = {'language_manager': language_manager}
100
100
 
101
101
  try:
102
- result: UpdateResult = await schema_category.update(pk, await request.json(), user, language_context)
102
+ result: UpdateResult = await schema_category.update(pk, await request.json(), user, language_manager)
103
103
  except AdminAPIException as e:
104
104
  return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
105
105
 
@@ -122,13 +122,13 @@ async def table_action(
122
122
  schema_category, user = await get_category(request, group, category, check_type=CategoryTable)
123
123
 
124
124
  language_slug = request.headers.get('Accept-Language')
125
- language_context: LanguageContext = schema.get_language_context(language_slug)
126
- context = {'language_context': language_context}
125
+ language_manager: LanguageManager = schema.get_language_manager(language_slug)
126
+ context = {'language_manager': language_manager}
127
127
 
128
128
  try:
129
129
  # pylint: disable=protected-access
130
130
  result: ActionResult = await schema_category._perform_action(
131
- request, action, action_data, language_context, user,
131
+ request, action, action_data, language_manager, user,
132
132
  )
133
133
  except AdminAPIException as e:
134
134
  return JSONResponse(e.get_error().model_dump(mode='json', context=context), status_code=e.status_code)
@@ -2,7 +2,7 @@ import abc
2
2
 
3
3
  from pydantic import BaseModel
4
4
  from pydantic.dataclasses import dataclass
5
- from brilliance_admin.utils import DataclassBase
5
+ from admin_panel.utils import DataclassBase
6
6
 
7
7
 
8
8
  @dataclass
@@ -3,12 +3,13 @@ from typing import Dict
3
3
  from pydantic import Field
4
4
  from pydantic.dataclasses import dataclass
5
5
 
6
- from brilliance_admin.utils import DataclassBase, SupportsStr
6
+ from admin_panel.translations import TranslateText
7
+ from admin_panel.utils import DataclassBase
7
8
 
8
9
 
9
10
  @dataclass
10
11
  class FieldError(DataclassBase, Exception):
11
- message: SupportsStr = None
12
+ message: str | TranslateText = None
12
13
  code: str | None = None
13
14
 
14
15
  def __post_init__(self):
@@ -19,7 +20,7 @@ class FieldError(DataclassBase, Exception):
19
20
 
20
21
  @dataclass
21
22
  class APIError(DataclassBase):
22
- message: SupportsStr | None = None
23
+ message: str | TranslateText | None = None
23
24
  code: str | None = None
24
25
  field_errors: Dict[str, FieldError] | None = None
25
26
 
@@ -1,7 +1,7 @@
1
- from brilliance_admin.auth import AdminAuthentication, AuthData, AuthResult, UserABC, UserResult
2
- from brilliance_admin.exceptions import AdminAPIException, APIError
3
- from brilliance_admin.translations import TranslateText as _
4
- from brilliance_admin.utils import get_logger
1
+ from admin_panel.auth import AdminAuthentication, AuthData, AuthResult, UserABC, UserResult
2
+ from admin_panel.exceptions import AdminAPIException, APIError
3
+ from admin_panel.translations import TranslateText as _
4
+ from admin_panel.utils import get_logger
5
5
 
6
6
  logger = get_logger()
7
7
 
@@ -1,11 +1,11 @@
1
- from brilliance_admin.auth import UserABC
2
- from brilliance_admin.schema.table.table_models import AutocompleteData, AutocompleteResult
3
- from brilliance_admin.translations import LanguageContext
1
+ from admin_panel.auth import UserABC
2
+ from admin_panel.schema.table.table_models import AutocompleteData, AutocompleteResult
3
+ from admin_panel.translations import LanguageManager
4
4
 
5
5
 
6
6
  class SQLAlchemyAdminAutocompleteMixin:
7
7
  async def autocomplete(
8
- self, data: AutocompleteData, user: UserABC, language_context: LanguageContext,
8
+ self, data: AutocompleteData, user: UserABC, language_manager: LanguageManager,
9
9
  ) -> AutocompleteResult:
10
10
  form_schema = None
11
11