brilliance-admin 0.42.0__py3-none-any.whl → 0.43.7__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.
- {admin_panel → brilliance_admin}/__init__.py +2 -2
- brilliance_admin/api/routers.py +18 -0
- {admin_panel → brilliance_admin}/api/utils.py +1 -1
- {admin_panel → brilliance_admin}/api/views/auth.py +6 -6
- {admin_panel → brilliance_admin}/api/views/autocomplete.py +9 -9
- {admin_panel → brilliance_admin}/api/views/graphs.py +8 -8
- {admin_panel → brilliance_admin}/api/views/index.py +11 -4
- {admin_panel → brilliance_admin}/api/views/schema.py +3 -3
- {admin_panel → brilliance_admin}/api/views/settings.py +5 -5
- {admin_panel → brilliance_admin}/api/views/table.py +23 -23
- {admin_panel → brilliance_admin}/auth.py +1 -1
- {admin_panel → brilliance_admin}/exceptions.py +3 -4
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/__init__.py +1 -0
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/auth.py +6 -6
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/autocomplete.py +10 -6
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/fields.py +24 -20
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/fields_schema.py +27 -18
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/table/base.py +8 -8
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/table/create.py +10 -10
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/table/delete.py +4 -4
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/table/list.py +11 -11
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/table/retrieve.py +22 -10
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/table/update.py +11 -11
- brilliance_admin/locales/en.yml +25 -0
- brilliance_admin/locales/ru.yml +26 -0
- {admin_panel → brilliance_admin}/schema/admin_schema.py +39 -31
- {admin_panel → brilliance_admin}/schema/category.py +8 -9
- {admin_panel → brilliance_admin}/schema/graphs/category_graphs.py +10 -9
- {admin_panel → brilliance_admin}/schema/group.py +10 -10
- {admin_panel → brilliance_admin}/schema/table/admin_action.py +8 -7
- {admin_panel → brilliance_admin}/schema/table/category_table.py +21 -21
- {admin_panel → brilliance_admin}/schema/table/fields/base.py +58 -29
- {admin_panel → brilliance_admin}/schema/table/fields/function_field.py +3 -3
- {admin_panel → brilliance_admin}/schema/table/fields_schema.py +11 -10
- {admin_panel → brilliance_admin}/schema/table/table_models.py +1 -1
- admin_panel/static/index-BeniOHDv.js → brilliance_admin/static/index-BnnESruI.js +131 -131
- {admin_panel → brilliance_admin}/templates/index.html +1 -1
- brilliance_admin/translations.py +115 -0
- brilliance_admin/utils.py +153 -0
- brilliance_admin-0.43.7.dist-info/METADATA +217 -0
- brilliance_admin-0.43.7.dist-info/RECORD +74 -0
- brilliance_admin-0.43.7.dist-info/top_level.txt +1 -0
- admin_panel/api/routers.py +0 -18
- admin_panel/static/favicon.jpg +0 -0
- admin_panel/translations.py +0 -145
- admin_panel/utils.py +0 -50
- brilliance_admin-0.42.0.dist-info/METADATA +0 -155
- brilliance_admin-0.42.0.dist-info/RECORD +0 -73
- brilliance_admin-0.42.0.dist-info/top_level.txt +0 -1
- {admin_panel → brilliance_admin}/api/__init__.py +0 -0
- {admin_panel → brilliance_admin}/api/views/__init__.py +0 -0
- {admin_panel → brilliance_admin}/docs.py +0 -0
- {admin_panel → brilliance_admin}/integrations/__init__.py +0 -0
- {admin_panel → brilliance_admin}/integrations/sqlalchemy/table/__init__.py +0 -0
- {admin_panel → brilliance_admin}/schema/__init__.py +0 -0
- {admin_panel → brilliance_admin}/schema/graphs/__init__.py +0 -0
- {admin_panel → brilliance_admin}/schema/table/__init__.py +0 -0
- {admin_panel → brilliance_admin}/schema/table/fields/__init__.py +0 -0
- {admin_panel → brilliance_admin}/static/index-vlBToOhT.css +0 -0
- {admin_panel → brilliance_admin}/static/materialdesignicons-webfont-CYDMK1kx.woff2 +0 -0
- {admin_panel → brilliance_admin}/static/materialdesignicons-webfont-CgCzGbLl.woff +0 -0
- {admin_panel → brilliance_admin}/static/materialdesignicons-webfont-D3kAzl71.ttf +0 -0
- {admin_panel → brilliance_admin}/static/materialdesignicons-webfont-DttUABo4.eot +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/dark-first/content.min.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/dark-first/skin.min.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/dark-slim/content.min.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/dark-slim/skin.min.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/img/example.png +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/img/tinymce.woff2 +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/lightgray/content.min.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/lightgray/fonts/tinymce.woff +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/lightgray/skin.min.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/plugins/accordion/css/accordion.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/plugins/accordion/plugin.js +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/plugins/codesample/css/prism.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/plugins/customLink/css/link.css +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/plugins/customLink/plugin.js +0 -0
- {admin_panel → brilliance_admin}/static/tinymce/tinymce.min.js +0 -0
- {admin_panel → brilliance_admin}/static/vanilla-picker-B6E6ObS_.js +0 -0
- {brilliance_admin-0.42.0.dist-info → brilliance_admin-0.43.7.dist-info}/WHEEL +0 -0
- {brilliance_admin-0.42.0.dist-info → brilliance_admin-0.43.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,22 +6,22 @@ from typing import Awaitable, List
|
|
|
6
6
|
from fastapi import HTTPException, Request
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from
|
|
9
|
+
from brilliance_admin.auth import UserABC
|
|
10
|
+
from brilliance_admin.exceptions import AdminAPIException, APIError
|
|
11
|
+
from brilliance_admin.schema import Category
|
|
12
|
+
from brilliance_admin.schema.category import TableInfoSchemaData
|
|
13
|
+
from brilliance_admin.schema.table.admin_action import ActionData, ActionResult
|
|
14
|
+
from brilliance_admin.schema.table.fields_schema import FieldsSchema
|
|
15
|
+
from brilliance_admin.schema.table.table_models import AutocompleteData, AutocompleteResult, ListData, TableListResult
|
|
16
|
+
from brilliance_admin.translations import LanguageContext
|
|
17
|
+
from brilliance_admin.utils import DeserializeAction, SupportsStr
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class CategoryTable(Category):
|
|
21
21
|
_type_slug: str = 'table'
|
|
22
22
|
|
|
23
23
|
search_enabled: bool = False
|
|
24
|
-
search_help:
|
|
24
|
+
search_help: SupportsStr | None = None
|
|
25
25
|
|
|
26
26
|
table_schema: FieldsSchema = None
|
|
27
27
|
table_filters: FieldsSchema | None = None
|
|
@@ -60,20 +60,20 @@ class CategoryTable(Category):
|
|
|
60
60
|
fn = getattr(self, 'update', None)
|
|
61
61
|
return asyncio.iscoroutinefunction(fn)
|
|
62
62
|
|
|
63
|
-
def generate_schema(self, user,
|
|
64
|
-
schema = super().generate_schema(user,
|
|
63
|
+
def generate_schema(self, user, language_context: LanguageContext) -> dict:
|
|
64
|
+
schema = super().generate_schema(user, language_context)
|
|
65
65
|
|
|
66
66
|
table_schema = getattr(self, 'table_schema', None)
|
|
67
67
|
if not table_schema or not issubclass(table_schema.__class__, FieldsSchema):
|
|
68
68
|
raise AttributeError(f'Admin category {self.__class__} must have table_schema instance of FieldsSchema')
|
|
69
69
|
|
|
70
70
|
table = TableInfoSchemaData(
|
|
71
|
-
table_schema=self.table_schema.generate_schema(user,
|
|
71
|
+
table_schema=self.table_schema.generate_schema(user, language_context),
|
|
72
72
|
ordering_fields=self.ordering_fields,
|
|
73
73
|
default_ordering=self.default_ordering,
|
|
74
74
|
|
|
75
75
|
search_enabled=self.search_enabled,
|
|
76
|
-
search_help=
|
|
76
|
+
search_help=language_context.get_text(self.search_help),
|
|
77
77
|
|
|
78
78
|
pk_name=self.pk_name,
|
|
79
79
|
can_retrieve=self.has_retrieve,
|
|
@@ -83,7 +83,7 @@ class CategoryTable(Category):
|
|
|
83
83
|
)
|
|
84
84
|
|
|
85
85
|
if self.table_filters:
|
|
86
|
-
table.table_filters = self.table_filters.generate_schema(user,
|
|
86
|
+
table.table_filters = self.table_filters.generate_schema(user, language_context)
|
|
87
87
|
|
|
88
88
|
actions = {}
|
|
89
89
|
for attribute_name in dir(self):
|
|
@@ -94,14 +94,14 @@ class CategoryTable(Category):
|
|
|
94
94
|
if asyncio.iscoroutinefunction(attribute) and getattr(attribute, '__action__', False):
|
|
95
95
|
action = copy.copy(attribute.action_info)
|
|
96
96
|
|
|
97
|
-
action['title'] =
|
|
98
|
-
action['description'] =
|
|
99
|
-
action['confirmation_text'] =
|
|
97
|
+
action['title'] = language_context.get_text(action.get('title'))
|
|
98
|
+
action['description'] = language_context.get_text(action.get('description'))
|
|
99
|
+
action['confirmation_text'] = language_context.get_text(action.get('confirmation_text'))
|
|
100
100
|
|
|
101
101
|
form_schema = action['form_schema']
|
|
102
102
|
if form_schema:
|
|
103
103
|
try:
|
|
104
|
-
action['form_schema'] = form_schema.generate_schema(user,
|
|
104
|
+
action['form_schema'] = form_schema.generate_schema(user, language_context)
|
|
105
105
|
except Exception as e:
|
|
106
106
|
msg = f'Action {attribute} form schema {form_schema} error: {e}'
|
|
107
107
|
raise Exception(msg) from e
|
|
@@ -126,7 +126,7 @@ class CategoryTable(Category):
|
|
|
126
126
|
request: Request,
|
|
127
127
|
action: str,
|
|
128
128
|
action_data: ActionData,
|
|
129
|
-
|
|
129
|
+
language_context: LanguageContext,
|
|
130
130
|
user: UserABC,
|
|
131
131
|
) -> ActionResult:
|
|
132
132
|
action_fn = self._get_action_fn(action)
|
|
@@ -162,7 +162,7 @@ class CategoryTable(Category):
|
|
|
162
162
|
|
|
163
163
|
# pylint: disable=too-many-arguments
|
|
164
164
|
@abc.abstractmethod
|
|
165
|
-
async def get_list(self, list_data: ListData, user: UserABC,
|
|
165
|
+
async def get_list(self, list_data: ListData, user: UserABC, language_context: LanguageContext) -> TableListResult:
|
|
166
166
|
raise NotImplementedError()
|
|
167
167
|
|
|
168
168
|
# async def retrieve(self, pk: Any, user: UserABC) -> RetrieveResult:
|
|
@@ -1,27 +1,28 @@
|
|
|
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
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
8
|
+
from brilliance_admin.exceptions import FieldError
|
|
9
|
+
from brilliance_admin.schema.category import FieldSchemaData
|
|
10
|
+
from brilliance_admin.translations import LanguageContext
|
|
11
|
+
from brilliance_admin.utils import DeserializeAction, SupportsStr, humanize_field_name
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
@dataclass
|
|
14
15
|
class TableField(abc.ABC, FieldSchemaData):
|
|
15
16
|
_type: ClassVar[str]
|
|
16
17
|
|
|
17
|
-
label:
|
|
18
|
-
help_text:
|
|
18
|
+
label: SupportsStr | None = None
|
|
19
|
+
help_text: SupportsStr | None = None
|
|
19
20
|
|
|
20
|
-
def generate_schema(self, user, field_slug,
|
|
21
|
+
def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
|
|
21
22
|
schema = FieldSchemaData(
|
|
22
23
|
type=self._type,
|
|
23
|
-
label=
|
|
24
|
-
help_text=
|
|
24
|
+
label=language_context.get_text(self.label) or humanize_field_name(field_slug),
|
|
25
|
+
help_text=language_context.get_text(self.help_text),
|
|
25
26
|
header=self.header,
|
|
26
27
|
read_only=self.read_only,
|
|
27
28
|
default=self.default,
|
|
@@ -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
|
|
|
@@ -59,10 +62,8 @@ class IntegerField(TableField):
|
|
|
59
62
|
precision: int | None = None
|
|
60
63
|
scale: int | None = None
|
|
61
64
|
|
|
62
|
-
def generate_schema(self, user, field_slug,
|
|
63
|
-
schema = super().generate_schema(user, field_slug,
|
|
64
|
-
|
|
65
|
-
schema.choices = self.choices
|
|
65
|
+
def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
|
|
66
|
+
schema = super().generate_schema(user, field_slug, language_context)
|
|
66
67
|
|
|
67
68
|
if self.max_value is not None:
|
|
68
69
|
schema.max_value = self.max_value
|
|
@@ -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
|
-
def generate_schema(self, user, field_slug,
|
|
101
|
-
schema = super().generate_schema(user, field_slug,
|
|
101
|
+
def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
|
|
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
|
|
|
@@ -135,8 +135,8 @@ class DateTimeField(TableField):
|
|
|
135
135
|
include_date: bool | None = True
|
|
136
136
|
include_time: bool | None = True
|
|
137
137
|
|
|
138
|
-
def generate_schema(self, user, field_slug,
|
|
139
|
-
schema = super().generate_schema(user, field_slug,
|
|
138
|
+
def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
|
|
139
|
+
schema = super().generate_schema(user, field_slug, language_context)
|
|
140
140
|
|
|
141
141
|
schema.range = self.range
|
|
142
142
|
schema.include_date = self.include_date
|
|
@@ -180,8 +180,8 @@ class ArrayField(TableField):
|
|
|
180
180
|
|
|
181
181
|
array_type: str | None
|
|
182
182
|
|
|
183
|
-
def generate_schema(self, user, field_slug,
|
|
184
|
-
schema = super().generate_schema(user, field_slug,
|
|
183
|
+
def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
|
|
184
|
+
schema = super().generate_schema(user, field_slug, language_context)
|
|
185
185
|
|
|
186
186
|
schema.array_type = self.array_type
|
|
187
187
|
|
|
@@ -208,8 +208,8 @@ class ImageField(TableField):
|
|
|
208
208
|
preview_max_height: int = 100
|
|
209
209
|
preview_max_width: int = 100
|
|
210
210
|
|
|
211
|
-
def generate_schema(self, user, field_slug,
|
|
212
|
-
schema = super().generate_schema(user, field_slug,
|
|
211
|
+
def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
|
|
212
|
+
schema = super().generate_schema(user, field_slug, language_context)
|
|
213
213
|
|
|
214
214
|
if self.preview_max_height is not None:
|
|
215
215
|
schema.preview_max_height = self.preview_max_height
|
|
@@ -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
|
|
|
237
|
-
def
|
|
238
|
-
|
|
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
|
+
|
|
260
|
+
def generate_schema(self, user, field_slug, language_context: LanguageContext) -> FieldSchemaData:
|
|
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
|
+
}
|
|
@@ -4,9 +4,9 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from pydantic.dataclasses import dataclass
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
7
|
+
from brilliance_admin.exceptions import AdminAPIException, APIError
|
|
8
|
+
from brilliance_admin.schema.table.fields.base import TableField
|
|
9
|
+
from brilliance_admin.utils import get_logger
|
|
10
10
|
|
|
11
11
|
logger = get_logger()
|
|
12
12
|
|
|
@@ -3,13 +3,13 @@ from typing import Any, ClassVar, Dict, List
|
|
|
3
3
|
|
|
4
4
|
from pydantic_core import core_schema
|
|
5
5
|
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
6
|
+
from brilliance_admin.auth import UserABC
|
|
7
|
+
from brilliance_admin.exceptions import AdminAPIException, APIError, FieldError
|
|
8
|
+
from brilliance_admin.schema.category import FieldSchemaData, FieldsSchemaData
|
|
9
|
+
from brilliance_admin.schema.table.fields.base import TableField
|
|
10
|
+
from brilliance_admin.schema.table.fields.function_field import FunctionField
|
|
11
|
+
from brilliance_admin.translations import LanguageContext
|
|
12
|
+
from brilliance_admin.utils import DeserializeAction
|
|
13
13
|
|
|
14
14
|
NOT_FUND_EXCEPTION = '''Field slug "{field_slug}" not found inside generated fields inside {class_name}
|
|
15
15
|
Available options: {available_fields}
|
|
@@ -143,14 +143,15 @@ class FieldsSchema:
|
|
|
143
143
|
def get_fields(self) -> Dict[str, TableField]:
|
|
144
144
|
return self._generated_fields
|
|
145
145
|
|
|
146
|
-
def generate_schema(self, user: UserABC,
|
|
146
|
+
def generate_schema(self, user: UserABC, language_context: LanguageContext) -> FieldsSchemaData:
|
|
147
147
|
fields_schema = FieldsSchemaData(
|
|
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
|
-
field_schema: FieldSchemaData = field.generate_schema(user, field_slug,
|
|
153
|
-
fields_schema.fields[field_slug] = field_schema.to_dict(keep_none=False)
|
|
153
|
+
field_schema: FieldSchemaData = field.generate_schema(user, field_slug, language_context)
|
|
154
|
+
fields_schema.fields[field_slug] = field_schema.to_dict(keep_none=False, context=context)
|
|
154
155
|
|
|
155
156
|
return fields_schema
|
|
156
157
|
|