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.
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/PKG-INFO +1 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/fields_schema.py +15 -12
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/base.py +4 -4
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/retrieve.py +3 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/category.py +0 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/base.py +37 -8
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields_schema.py +2 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/translations.py +3 -3
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/utils.py +3 -3
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/PKG-INFO +1 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/locales/en.yml +5 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/locales/ru.yml +5 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/models.py +35 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/payments.py +0 -13
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/terminal.py +1 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/pyproject.toml +1 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_payments_fields_schema.py +0 -29
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_crud.py +18 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_schema.py +28 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/uv.lock +1 -1
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.configs/docker/Dockerfile +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.configs/docker/docker-compose.yml +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.configs/nginx/example.conf +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.env +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.github/workflows/certbot.yml +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.github/workflows/deploy.yml +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.github/workflows/install-docker.yml +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.gitignore +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.isort.cfg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/.python-version +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/LICENSE +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/README.md +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/routers.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/utils.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/auth.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/autocomplete.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/graphs.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/index.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/schema.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/settings.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/table.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/auth.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/docs.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/exceptions.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/auth.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/autocomplete.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/fields.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/create.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/delete.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/list.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/table/update.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/locales/en.yml +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/locales/ru.yml +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/admin_schema.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/graphs/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/graphs/category_graphs.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/group.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/admin_action.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/category_table.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/function_field.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/table_models.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/index-D9axz5zK.js +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/index-vlBToOhT.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-first/content.min.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-first/skin.min.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-slim/content.min.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/dark-slim/skin.min.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/img/example.png +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/img/tinymce.woff2 +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/lightgray/content.min.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/lightgray/skin.min.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/accordion/css/accordion.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/accordion/plugin.js +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/codesample/css/prism.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/customLink/css/link.css +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/plugins/customLink/plugin.js +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/tinymce.min.js +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/vanilla-picker-B6E6ObS_.js +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/templates/index.html +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/SOURCES.txt +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/dependency_links.txt +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/requires.txt +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/top_level.txt +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/README.md +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/main.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/currency.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/graphs.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/merchant.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sections/users.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/sqlite.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/favicon.ico +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/favicon.jpg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/logo-outline.png +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/static/logo.png +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/example/utils.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/PC-graphs.jpeg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/PC-table.jpeg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/iPad-edit.jpeg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/iPhone 15-edit.jpeg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/iPhone 15-login.jpeg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/screenshots/websitemockupgenerator.png +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/setup.cfg +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/__init__.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/conftest.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_action.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_settings.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_auth.py +0 -0
- {brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/tests/test_sqlalcmeny_filters.py +0 -0
- {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.
|
|
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
|
-
|
|
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"] =
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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,
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/base.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import datetime
|
|
3
|
-
from
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields_schema.py
RENAMED
|
@@ -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.
|
|
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.
|
|
70
|
+
logger.debug('Language manager locales_dir loaded: %s', locales_dir)
|
|
71
71
|
|
|
72
72
|
langs = ', '.join(self.phrases.data.keys())
|
|
73
|
-
logger.
|
|
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
|
-
|
|
153
|
+
return slug
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brilliance-admin
|
|
3
|
-
Version: 0.43.
|
|
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 =
|
|
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',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "brilliance-admin"
|
|
3
|
-
version = "0.43.
|
|
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
|
|
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',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/api/views/autocomplete.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/integrations/sqlalchemy/auth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/graphs/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/__init__.py
RENAMED
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/admin_action.py
RENAMED
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/category_table.py
RENAMED
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/fields/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/schema/table/table_models.py
RENAMED
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/index-D9axz5zK.js
RENAMED
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/index-vlBToOhT.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/img/example.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin/static/tinymce/tinymce.min.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brilliance_admin-0.43.2 → brilliance_admin-0.43.4}/brilliance_admin.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|