Module licenseware.editable_table.editable_table
EditableTable provided in this package is used to generate table metadata from a marshmallow Schema
From the schema provided EditableTable(schema).specs
will return a list of editable tables metadata similar to this:
[
{
"component_id": "ifmp_devicetables",
"url": "http://localhost:5000/ifmp/devicetable",
"path": "/devicetable",
"order": 1,
"style_attributes": {
"width": "full"
},
"title": "All devicetables",
"type": "editable_table",
"columns": [
{
"name": "Id",
"prop": "_id",
"editable": false,
"type": "string",
"values": null,
"required": false,
"visible": false,
"entities_url": "http://localhost:5000/ifmp/devicetable?_id=%7Bentity_id%7D",
"entities_path": "/devicetable?_id=%7Bentity_id%7D"
},
{
"name": "Is Parent To",
"prop": "is_parent_to",
"editable": true,
"type": "entity",
"values": null,
"required": false,
"visible": false,
"entities_url": "http://localhost:5000/ifmp/devicetable?distinct_key=name&foreign_key=name&_id=%7Bentity_id%7D",
"entities_path": "/devicetable?distinct_key=name&foreign_key=name&_id=%7Bentity_id%7D"
},
{
"name": "Device Type",
"prop": "device_type",
"editable": false,
"type": "enum",
"values": [
"Cluster",
"Domain",
"Physical",
"Pool",
"Unknown",
"Virtual"
],
"required": true,
"visible": false,
"entities_url": "http://localhost:5000/ifmp/devicetable?_id=%7Bentity_id%7D",
"entities_path": "/devicetable?_id=%7Bentity_id%7D"
},
]
}
]
From the information provided up the front-end will know how to render each column based on: datatype(type), editable, required, visible parameters.
TODO add pagination
By making a get request to the root url ("url": "http://localhost:5000/ifmp/devicetable"
) all data will be returned from mongo.
A get request with the _id
query parameter will return the coresponding document from mongo.
"entities_url": "http://localhost:5000/ifmp/devicetable?_id=%7Bentity_id%7D"
(%7D
it's }
with urlencode)
A post/put/delete request to root url with the modified/new document will modifiy the document.
NOTE The query params and payload will be merged and used as a mongo query! Any data manipulation operation can be done from the front-end.
TODO explore security risks
Bellow we have an example of using a schema to generate an editable table metadata and it's coresponding api.
We provide the information required for generating editable metadata in the metadata
parameter.
Column names will be created from field names and other information's such as datatype, values(in case of enum
datatype) etc will be taken from marshmallow fields
specifications.
For metadata you can use either a dict or metaspecs
function which has autocomplete.
from marshmallow import Schema, fields, validate
from licenseware.common.constants import envs
from licenseware.editable_table import EditableTable, metaspecs
from licenseware.schema_namespace import MongoCrud, SchemaNamespace
from app.common.infrastructure_service import InfraService
class DeviceTableSchema(Schema):
''' IFMP devices editable table ''' # this will be the api doc
class Meta:
compound_indexes = [
['tenant_id', 'name'],
['tenant_id', 'name', 'device_type']
]
simple_indexes = ['_id', 'tenant_id', 'name',
'is_parent_to', 'is_child_to',
'is_part_of_cluster_with', 'is_dr_with',
'device_type', 'virtualization_type',
'cpu_model'
]
_id = fields.Str(required=False, unique=True)
tenant_id = fields.Str(required=True)
name = fields.Str(required=True, unique=False, metadata=metaspecs(editable=True))
is_parent_to = fields.List(
fields.Str(), required=False, allow_none=True,
metadata={'editable': True, 'distinct_key': 'name', 'foreign_key': 'name'}
)
is_child_to = fields.Str(
required=False, allow_none=True,
metadata=metaspecs(editable=True, distinct_key='name', foreign_key='name')
)
is_part_of_cluster_with = fields.List(
fields.Str(), required=False, allow_none=True,
metadata={'editable': True, 'distinct_key': 'name', 'foreign_key': 'name'}
)
is_dr_with = fields.List(
fields.Str(), required=False, allow_none=True,
metadata={'editable': True, 'distinct_key': 'name', 'foreign_key': 'name'}
)
capped = fields.Boolean(required=True, allow_none=False, metadata={'editable': True})
total_number_of_processors = fields.Integer(required=False, allow_none=True, metadata={'editable': True})
manufacturer = fields.Str(required=False, allow_none=True, metadata={'editable': True})
model = fields.Str(required=False, allow_none=True, metadata={'editable': True})
updated_at = fields.Str(required=False)
raw_data = fields.Str(required=False, allow_none=True)
source = fields.Str(required=False, allow_none=True, metadata={'editable': True})
source_system_id = fields.Str(required=False, allow_none=True, metadata={'editable': True})
# here we are inheriting from `MongoCrud` class which has default crud methods
# we are overwriting the `put_data` method
class DeviceOp(MongoCrud):
def __init__(self, schema: Schema, collection: str):
self.schema = schema
self.collection = collection
super().__init__(schema, collection)
def put_data(self, flask_request):
query = self.get_query(flask_request)
return InfraService(
schema=self.schema,
collection=self.collection
).replace_one(json_data=query)
# Here we are creating a restx namespace using the schema
DeviceNs = SchemaNamespace(
schema=DeviceTableSchema,
collection=envs.MONGO_COLLECTION_DATA_NAME, #default can be skipped
methods=['GET', 'PUT', 'DELETE'], #default is ['GET', 'PUT', 'POST', 'DELETE']
mongo_crud_class = DeviceOp # here we provide the modified crud operations class
).initialize()
# In the case of a crud operation overwrite we need to provide to `EditableTable` the restx namespace created up
devices_table = EditableTable(
title="All Devices",
schema=DeviceTableSchema,
namespace=DeviceNs # needed in case of an overwrite
)
# If no overwrites are necessary providing just the schema will be suficient
devices_table = EditableTable(
title="All Devices",
schema=DeviceTableSchema
)
Later, in the base __init__.py
file import the EditableTable
instance and register it to the main App
.
from licenseware.common.constants import flags
from licenseware.app_builder import AppBuilder
from app.controllers.device_editable_controller import devices_table
App = AppBuilder(
name = 'Infrastructure Mapper',
description = 'IFMP (Infrastructure Mapper) is the ideal tool to help you get a complete picture of your entire infrastructure topology with comprehensive CPU, virtualization, and relationship details. You can combine IFMP data with data from other apps like ODBM (Oracle Database Manager) to generate consolidated reports showing your actual license utilization across your infrastructure.',
flags = [flags.BETA]
)
App.register_editable_table(devices_table)
Expand source code
"""
EditableTable provided in this package is used to generate table metadata from a marshmallow Schema
From the schema provided `EditableTable(schema).specs` will return a list of editable tables metadata similar to this:
```js
[
{
"component_id": "ifmp_devicetables",
"url": "http://localhost:5000/ifmp/devicetable",
"path": "/devicetable",
"order": 1,
"style_attributes": {
"width": "full"
},
"title": "All devicetables",
"type": "editable_table",
"columns": [
{
"name": "Id",
"prop": "_id",
"editable": false,
"type": "string",
"values": null,
"required": false,
"visible": false,
"entities_url": "http://localhost:5000/ifmp/devicetable?_id=%7Bentity_id%7D",
"entities_path": "/devicetable?_id=%7Bentity_id%7D"
},
{
"name": "Is Parent To",
"prop": "is_parent_to",
"editable": true,
"type": "entity",
"values": null,
"required": false,
"visible": false,
"entities_url": "http://localhost:5000/ifmp/devicetable?distinct_key=name&foreign_key=name&_id=%7Bentity_id%7D",
"entities_path": "/devicetable?distinct_key=name&foreign_key=name&_id=%7Bentity_id%7D"
},
{
"name": "Device Type",
"prop": "device_type",
"editable": false,
"type": "enum",
"values": [
"Cluster",
"Domain",
"Physical",
"Pool",
"Unknown",
"Virtual"
],
"required": true,
"visible": false,
"entities_url": "http://localhost:5000/ifmp/devicetable?_id=%7Bentity_id%7D",
"entities_path": "/devicetable?_id=%7Bentity_id%7D"
},
]
}
]
```
From the information provided up the front-end will know how to render each column based on: datatype(type), editable, required, visible parameters.
# TODO add pagination
By making a get request to the root url (`"url": "http://localhost:5000/ifmp/devicetable"`) all data will be returned from mongo.
A get request with the `_id` query parameter will return the coresponding document from mongo.
`"entities_url": "http://localhost:5000/ifmp/devicetable?_id=%7Bentity_id%7D"` (`%7D` it's `}` with urlencode)
A post/put/delete request to root url with the modified/new document will modifiy the document.
**NOTE**
The query params and payload will be merged and used as a mongo query!
Any data manipulation operation can be done from the front-end.
# TODO explore security risks
Bellow we have an example of using a schema to generate an editable table metadata and it's coresponding api.
We provide the information required for generating editable metadata in the `metadata` parameter.
Column names will be created from field names and other information's such as datatype, values(in case of `enum` datatype) etc will be taken from marshmallow `fields` specifications.
For metadata you can use either a dict or `metaspecs` function which has autocomplete.
```py
from marshmallow import Schema, fields, validate
from licenseware.common.constants import envs
from licenseware.editable_table import EditableTable, metaspecs
from licenseware.schema_namespace import MongoCrud, SchemaNamespace
from app.common.infrastructure_service import InfraService
class DeviceTableSchema(Schema):
''' IFMP devices editable table ''' # this will be the api doc
class Meta:
compound_indexes = [
['tenant_id', 'name'],
['tenant_id', 'name', 'device_type']
]
simple_indexes = ['_id', 'tenant_id', 'name',
'is_parent_to', 'is_child_to',
'is_part_of_cluster_with', 'is_dr_with',
'device_type', 'virtualization_type',
'cpu_model'
]
_id = fields.Str(required=False, unique=True)
tenant_id = fields.Str(required=True)
name = fields.Str(required=True, unique=False, metadata=metaspecs(editable=True))
is_parent_to = fields.List(
fields.Str(), required=False, allow_none=True,
metadata={'editable': True, 'distinct_key': 'name', 'foreign_key': 'name'}
)
is_child_to = fields.Str(
required=False, allow_none=True,
metadata=metaspecs(editable=True, distinct_key='name', foreign_key='name')
)
is_part_of_cluster_with = fields.List(
fields.Str(), required=False, allow_none=True,
metadata={'editable': True, 'distinct_key': 'name', 'foreign_key': 'name'}
)
is_dr_with = fields.List(
fields.Str(), required=False, allow_none=True,
metadata={'editable': True, 'distinct_key': 'name', 'foreign_key': 'name'}
)
capped = fields.Boolean(required=True, allow_none=False, metadata={'editable': True})
total_number_of_processors = fields.Integer(required=False, allow_none=True, metadata={'editable': True})
manufacturer = fields.Str(required=False, allow_none=True, metadata={'editable': True})
model = fields.Str(required=False, allow_none=True, metadata={'editable': True})
updated_at = fields.Str(required=False)
raw_data = fields.Str(required=False, allow_none=True)
source = fields.Str(required=False, allow_none=True, metadata={'editable': True})
source_system_id = fields.Str(required=False, allow_none=True, metadata={'editable': True})
# here we are inheriting from `MongoCrud` class which has default crud methods
# we are overwriting the `put_data` method
class DeviceOp(MongoCrud):
def __init__(self, schema: Schema, collection: str):
self.schema = schema
self.collection = collection
super().__init__(schema, collection)
def put_data(self, flask_request):
query = self.get_query(flask_request)
return InfraService(
schema=self.schema,
collection=self.collection
).replace_one(json_data=query)
# Here we are creating a restx namespace using the schema
DeviceNs = SchemaNamespace(
schema=DeviceTableSchema,
collection=envs.MONGO_COLLECTION_DATA_NAME, #default can be skipped
methods=['GET', 'PUT', 'DELETE'], #default is ['GET', 'PUT', 'POST', 'DELETE']
mongo_crud_class = DeviceOp # here we provide the modified crud operations class
).initialize()
# In the case of a crud operation overwrite we need to provide to `EditableTable` the restx namespace created up
devices_table = EditableTable(
title="All Devices",
schema=DeviceTableSchema,
namespace=DeviceNs # needed in case of an overwrite
)
# If no overwrites are necessary providing just the schema will be suficient
devices_table = EditableTable(
title="All Devices",
schema=DeviceTableSchema
)
```
Later, in the base `__init__.py` file import the `EditableTable` instance and register it to the main `App`.
```py
from licenseware.common.constants import flags
from licenseware.app_builder import AppBuilder
from app.controllers.device_editable_controller import devices_table
App = AppBuilder(
name = 'Infrastructure Mapper',
description = 'IFMP (Infrastructure Mapper) is the ideal tool to help you get a complete picture of your entire infrastructure topology with comprehensive CPU, virtualization, and relationship details. You can combine IFMP data with data from other apps like ODBM (Oracle Database Manager) to generate consolidated reports showing your actual license utilization across your infrastructure.',
flags = [flags.BETA]
)
App.register_editable_table(devices_table)
```
"""
import re, itertools
from flask_restx import Namespace
import marshmallow
from marshmallow import Schema
from licenseware.common.constants import envs
from urllib.parse import urlencode
from typing import List
from licenseware.utils.logger import log
class EditableTable:
def __init__(
self,
schema: type,
title: str = None,
namespace: Namespace = None,
component_id: str = None,
url: str = None,
table_type: str = "editable_table",
order: int = 1,
style_attributes: dict = {'width': 'full'}
):
self.schema = schema
self.namespace = namespace
if "Table" not in self.schema.__name__:
raise ValueError(
"Schema provided to editable tables must contain in it's name 'Table' keyword (ex: DeviceTableSchema)")
self.schema_name = self.schema.__name__.replace('Schema', '').lower()
self.names = self.schema_name
self.component_id = component_id or self.component_id_from_schema()
self.title = title or self.title_from_schema()
self.path = (url or self.url_from_schema())
self.url = envs.BASE_URL + self.path
self.table_type = table_type
self.order = order
self.style_attributes = style_attributes
self.schema_dict = self.make_schema_dict()
self.add_title_on_schema_meta()
def add_title_on_schema_meta(self):
""" Adding title on Meta if not present or getting title if exists """
if hasattr(self.schema, 'Meta'):
if hasattr(self.schema.Meta, 'title'):
self.title = self.schema.Meta.title
else:
self.schema = type(
self.schema.__name__, (self.schema,),
{'Meta': type('Meta', (self.schema.Meta,), {'title': self.title})}
)
else:
self.schema = type(
self.schema.__name__,
(self.schema,),
{'Meta': type('Meta', (), {'title': self.title})}
)
def url_from_schema(self):
return f'/{self.schema_name}'
def title_from_schema(self):
return self.names
def component_id_from_schema(self):
return envs.APP_ID + "_" + self.names
def make_schema_dict(self):
field_dict = lambda data: {
k: v for k, v in data.__dict__.items()
if k not in ['default', '_creation_index', 'missing', 'inner']
}
schema_dict = lambda declared_fields: {
field: field_dict(data)
for field, data in declared_fields.items()
}
return schema_dict(self.schema._declared_fields)
@property
def specs(self):
return self.get_specifications()
def get_specifications(self):
return {
"component_id": self.component_id,
"url": self.url,
"path": self.path,
"order": self.order,
"style_attributes": self.style_attributes,
"title": self.title,
"type": self.table_type,
"columns": self.columns_spec_list()
}
def columns_spec_list(self):
columns_list = []
for field_name, field_data in self.schema_dict.items():
columns_list.append({
"name": self.col_name(field_name),
"prop": self.col_prop(field_name),
"editable": self.col_editable(field_data),
"type": self.col_type(field_data),
"values": self.col_enum_values(field_data),
"required": self.col_required(field_data),
"visible": self.col_visible(field_name, field_data),
"hashable": self.col_hashable(field_name, field_data),
"entities_url": self.col_entities_url(field_data)
})
return columns_list
def col_entities_url(self, field_data, _get_only_path=False):
"""
_id - device(doc) id which contains foreign_keys to get the distinct_keys
foreign_key - field name that contains ids to distinct_key
metadata={'editable': False, 'distinct_key': 'name', 'foreign_key': 'is_parent_to'}
"""
metadata = self.field_metadata(field_data)
if 'foreign_key' in metadata and metadata['foreign_key'] != None:
params = urlencode({
'foreign_key': metadata['foreign_key'],
'_id': '{entity_id}'
})
return f"{self.path}?{params}" if _get_only_path else f"{self.url}?{params}"
return None
def col_entities_path(self, field_data):
return self.col_entities_url(field_data, _get_only_path=True)
def col_required(self, field_data):
return field_data['required']
def col_visible(self, field_name, field_data):
metadata = self.field_metadata(field_data)
if 'visible' in metadata: return metadata['visible']
if field_name.startswith('_'): return False
if field_name in ['tenant_id']: return False
return False
def col_hashable(self, field_name, field_data):
metadata = self.field_metadata(field_data)
if 'hashable' in metadata: return metadata['hashable']
if field_name.startswith('_'): return False
if field_name in ['tenant_id']: return False
return False
def col_enum_values(self, field_data):
try:
if field_data['validate'] is None: return
if isinstance(field_data['validate'], marshmallow.validate.OneOf):
return field_data['validate'].choices
if isinstance(field_data['validate'], list):
return sorted(list(set(itertools.chain(*[data.choices for data in field_data['validate']]))))
except Exception as err:
# log.error(err)
return None
def col_name(self, field_name):
return " ".join([f.capitalize() for f in field_name.split('_') if f != ""])
def col_prop(self, field_name):
return field_name
def col_editable(self, field_data):
metadata = self.field_metadata(field_data)
if 'editable' in metadata: return metadata['editable']
return False
def col_type(self, field_data):
metadata = self.field_metadata(field_data)
if 'type' in metadata: return metadata['type']
if 'distinct_key' in metadata: return 'entity'
try:
if isinstance(field_data['validate'], marshmallow.validate.OneOf):
if len(field_data['validate'].choices) > 0:
return 'enum'
if isinstance(field_data['validate'], list):
for data in field_data['validate']:
if len(data['validate'].choices) > 0:
return 'enum'
except:
...
try:
invalid_message = field_data['error_messages']['invalid']
return re.search(r'Not a valid (.*?)\.', invalid_message).group(1).lower()
except:
...
def field_metadata(self, field_data):
if 'metadata' in field_data:
return field_data['metadata']
return ""
def editable_tables_from_schemas(schemas_list: List[Schema]) -> List[dict]:
editable_tables = []
for schema in schemas_list:
table = EditableTable(schema)
editable_tables.append(table.specs)
return editable_tables
Functions
def editable_tables_from_schemas(schemas_list: List[marshmallow.schema.Schema]) ‑> List[dict]
-
Expand source code
def editable_tables_from_schemas(schemas_list: List[Schema]) -> List[dict]: editable_tables = [] for schema in schemas_list: table = EditableTable(schema) editable_tables.append(table.specs) return editable_tables
Classes
class EditableTable (schema: type, title: str = None, namespace: flask_restx.namespace.Namespace = None, component_id: str = None, url: str = None, table_type: str = 'editable_table', order: int = 1, style_attributes: dict = {'width': 'full'})
-
Expand source code
class EditableTable: def __init__( self, schema: type, title: str = None, namespace: Namespace = None, component_id: str = None, url: str = None, table_type: str = "editable_table", order: int = 1, style_attributes: dict = {'width': 'full'} ): self.schema = schema self.namespace = namespace if "Table" not in self.schema.__name__: raise ValueError( "Schema provided to editable tables must contain in it's name 'Table' keyword (ex: DeviceTableSchema)") self.schema_name = self.schema.__name__.replace('Schema', '').lower() self.names = self.schema_name self.component_id = component_id or self.component_id_from_schema() self.title = title or self.title_from_schema() self.path = (url or self.url_from_schema()) self.url = envs.BASE_URL + self.path self.table_type = table_type self.order = order self.style_attributes = style_attributes self.schema_dict = self.make_schema_dict() self.add_title_on_schema_meta() def add_title_on_schema_meta(self): """ Adding title on Meta if not present or getting title if exists """ if hasattr(self.schema, 'Meta'): if hasattr(self.schema.Meta, 'title'): self.title = self.schema.Meta.title else: self.schema = type( self.schema.__name__, (self.schema,), {'Meta': type('Meta', (self.schema.Meta,), {'title': self.title})} ) else: self.schema = type( self.schema.__name__, (self.schema,), {'Meta': type('Meta', (), {'title': self.title})} ) def url_from_schema(self): return f'/{self.schema_name}' def title_from_schema(self): return self.names def component_id_from_schema(self): return envs.APP_ID + "_" + self.names def make_schema_dict(self): field_dict = lambda data: { k: v for k, v in data.__dict__.items() if k not in ['default', '_creation_index', 'missing', 'inner'] } schema_dict = lambda declared_fields: { field: field_dict(data) for field, data in declared_fields.items() } return schema_dict(self.schema._declared_fields) @property def specs(self): return self.get_specifications() def get_specifications(self): return { "component_id": self.component_id, "url": self.url, "path": self.path, "order": self.order, "style_attributes": self.style_attributes, "title": self.title, "type": self.table_type, "columns": self.columns_spec_list() } def columns_spec_list(self): columns_list = [] for field_name, field_data in self.schema_dict.items(): columns_list.append({ "name": self.col_name(field_name), "prop": self.col_prop(field_name), "editable": self.col_editable(field_data), "type": self.col_type(field_data), "values": self.col_enum_values(field_data), "required": self.col_required(field_data), "visible": self.col_visible(field_name, field_data), "hashable": self.col_hashable(field_name, field_data), "entities_url": self.col_entities_url(field_data) }) return columns_list def col_entities_url(self, field_data, _get_only_path=False): """ _id - device(doc) id which contains foreign_keys to get the distinct_keys foreign_key - field name that contains ids to distinct_key metadata={'editable': False, 'distinct_key': 'name', 'foreign_key': 'is_parent_to'} """ metadata = self.field_metadata(field_data) if 'foreign_key' in metadata and metadata['foreign_key'] != None: params = urlencode({ 'foreign_key': metadata['foreign_key'], '_id': '{entity_id}' }) return f"{self.path}?{params}" if _get_only_path else f"{self.url}?{params}" return None def col_entities_path(self, field_data): return self.col_entities_url(field_data, _get_only_path=True) def col_required(self, field_data): return field_data['required'] def col_visible(self, field_name, field_data): metadata = self.field_metadata(field_data) if 'visible' in metadata: return metadata['visible'] if field_name.startswith('_'): return False if field_name in ['tenant_id']: return False return False def col_hashable(self, field_name, field_data): metadata = self.field_metadata(field_data) if 'hashable' in metadata: return metadata['hashable'] if field_name.startswith('_'): return False if field_name in ['tenant_id']: return False return False def col_enum_values(self, field_data): try: if field_data['validate'] is None: return if isinstance(field_data['validate'], marshmallow.validate.OneOf): return field_data['validate'].choices if isinstance(field_data['validate'], list): return sorted(list(set(itertools.chain(*[data.choices for data in field_data['validate']])))) except Exception as err: # log.error(err) return None def col_name(self, field_name): return " ".join([f.capitalize() for f in field_name.split('_') if f != ""]) def col_prop(self, field_name): return field_name def col_editable(self, field_data): metadata = self.field_metadata(field_data) if 'editable' in metadata: return metadata['editable'] return False def col_type(self, field_data): metadata = self.field_metadata(field_data) if 'type' in metadata: return metadata['type'] if 'distinct_key' in metadata: return 'entity' try: if isinstance(field_data['validate'], marshmallow.validate.OneOf): if len(field_data['validate'].choices) > 0: return 'enum' if isinstance(field_data['validate'], list): for data in field_data['validate']: if len(data['validate'].choices) > 0: return 'enum' except: ... try: invalid_message = field_data['error_messages']['invalid'] return re.search(r'Not a valid (.*?)\.', invalid_message).group(1).lower() except: ... def field_metadata(self, field_data): if 'metadata' in field_data: return field_data['metadata'] return ""
Instance variables
var specs
-
Expand source code
@property def specs(self): return self.get_specifications()
Methods
def add_title_on_schema_meta(self)
-
Adding title on Meta if not present or getting title if exists
Expand source code
def add_title_on_schema_meta(self): """ Adding title on Meta if not present or getting title if exists """ if hasattr(self.schema, 'Meta'): if hasattr(self.schema.Meta, 'title'): self.title = self.schema.Meta.title else: self.schema = type( self.schema.__name__, (self.schema,), {'Meta': type('Meta', (self.schema.Meta,), {'title': self.title})} ) else: self.schema = type( self.schema.__name__, (self.schema,), {'Meta': type('Meta', (), {'title': self.title})} )
def col_editable(self, field_data)
-
Expand source code
def col_editable(self, field_data): metadata = self.field_metadata(field_data) if 'editable' in metadata: return metadata['editable'] return False
def col_entities_path(self, field_data)
-
Expand source code
def col_entities_path(self, field_data): return self.col_entities_url(field_data, _get_only_path=True)
def col_entities_url(self, field_data)
-
_id - device(doc) id which contains foreign_keys to get the distinct_keys foreign_key - field name that contains ids to distinct_key metadata={'editable': False, 'distinct_key': 'name', 'foreign_key': 'is_parent_to'}
Expand source code
def col_entities_url(self, field_data, _get_only_path=False): """ _id - device(doc) id which contains foreign_keys to get the distinct_keys foreign_key - field name that contains ids to distinct_key metadata={'editable': False, 'distinct_key': 'name', 'foreign_key': 'is_parent_to'} """ metadata = self.field_metadata(field_data) if 'foreign_key' in metadata and metadata['foreign_key'] != None: params = urlencode({ 'foreign_key': metadata['foreign_key'], '_id': '{entity_id}' }) return f"{self.path}?{params}" if _get_only_path else f"{self.url}?{params}" return None
def col_enum_values(self, field_data)
-
Expand source code
def col_enum_values(self, field_data): try: if field_data['validate'] is None: return if isinstance(field_data['validate'], marshmallow.validate.OneOf): return field_data['validate'].choices if isinstance(field_data['validate'], list): return sorted(list(set(itertools.chain(*[data.choices for data in field_data['validate']])))) except Exception as err: # log.error(err) return None
def col_hashable(self, field_name, field_data)
-
Expand source code
def col_hashable(self, field_name, field_data): metadata = self.field_metadata(field_data) if 'hashable' in metadata: return metadata['hashable'] if field_name.startswith('_'): return False if field_name in ['tenant_id']: return False return False
def col_name(self, field_name)
-
Expand source code
def col_name(self, field_name): return " ".join([f.capitalize() for f in field_name.split('_') if f != ""])
def col_prop(self, field_name)
-
Expand source code
def col_prop(self, field_name): return field_name
def col_required(self, field_data)
-
Expand source code
def col_required(self, field_data): return field_data['required']
def col_type(self, field_data)
-
Expand source code
def col_type(self, field_data): metadata = self.field_metadata(field_data) if 'type' in metadata: return metadata['type'] if 'distinct_key' in metadata: return 'entity' try: if isinstance(field_data['validate'], marshmallow.validate.OneOf): if len(field_data['validate'].choices) > 0: return 'enum' if isinstance(field_data['validate'], list): for data in field_data['validate']: if len(data['validate'].choices) > 0: return 'enum' except: ... try: invalid_message = field_data['error_messages']['invalid'] return re.search(r'Not a valid (.*?)\.', invalid_message).group(1).lower() except: ...
def col_visible(self, field_name, field_data)
-
Expand source code
def col_visible(self, field_name, field_data): metadata = self.field_metadata(field_data) if 'visible' in metadata: return metadata['visible'] if field_name.startswith('_'): return False if field_name in ['tenant_id']: return False return False
def columns_spec_list(self)
-
Expand source code
def columns_spec_list(self): columns_list = [] for field_name, field_data in self.schema_dict.items(): columns_list.append({ "name": self.col_name(field_name), "prop": self.col_prop(field_name), "editable": self.col_editable(field_data), "type": self.col_type(field_data), "values": self.col_enum_values(field_data), "required": self.col_required(field_data), "visible": self.col_visible(field_name, field_data), "hashable": self.col_hashable(field_name, field_data), "entities_url": self.col_entities_url(field_data) }) return columns_list
def component_id_from_schema(self)
-
Expand source code
def component_id_from_schema(self): return envs.APP_ID + "_" + self.names
def field_metadata(self, field_data)
-
Expand source code
def field_metadata(self, field_data): if 'metadata' in field_data: return field_data['metadata'] return ""
def get_specifications(self)
-
Expand source code
def get_specifications(self): return { "component_id": self.component_id, "url": self.url, "path": self.path, "order": self.order, "style_attributes": self.style_attributes, "title": self.title, "type": self.table_type, "columns": self.columns_spec_list() }
def make_schema_dict(self)
-
Expand source code
def make_schema_dict(self): field_dict = lambda data: { k: v for k, v in data.__dict__.items() if k not in ['default', '_creation_index', 'missing', 'inner'] } schema_dict = lambda declared_fields: { field: field_dict(data) for field, data in declared_fields.items() } return schema_dict(self.schema._declared_fields)
def title_from_schema(self)
-
Expand source code
def title_from_schema(self): return self.names
def url_from_schema(self)
-
Expand source code
def url_from_schema(self): return f'/{self.schema_name}'