Module: panel_response_util
Expand source code
# Copyright (C) 2023-present The Project Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import ast
import base64
import dataclasses
import io
from typing import Any
from typing import Dict
from typing import List
from pydantic import BaseModel
from cl.runtime.context.context import Context
from cl.runtime.plots.plot_key import PlotKey
from cl.runtime.routers.entity.panel_request import PanelRequest
from cl.runtime.routers.response_util import to_legacy_dict
from cl.runtime.routers.response_util import to_record_dict
from cl.runtime.schema.handler_declare_block_decl import HandlerDeclareBlockDecl
from cl.runtime.schema.schema import Schema
from cl.runtime.serialization.string_serializer import StringSerializer
from cl.runtime.serialization.ui_dict_serializer import UiDictSerializer
from cl.runtime.view.dag.dag import Dag
from cl.runtime.views.binary_content import BinaryContent
from cl.runtime.views.html_view import HtmlView
from cl.runtime.views.key_view import KeyView
from cl.runtime.views.pdf_view import PdfView
from cl.runtime.views.plot_view import PlotView
from cl.runtime.views.png_view import PngView
from cl.runtime.views.script import Script
PanelResponseData = Dict[str, Any] | List[Dict[str, Any]] | None
ui_serializer = UiDictSerializer()
"""Ui serializer."""
class PanelResponseUtil(BaseModel):
"""Response util for the /entity/panel route."""
view_of: PanelResponseData
"""Response data type for the /entity/panel route."""
@classmethod
def get_content(cls, request: PanelRequest) -> Dict[str, PanelResponseData]:
"""Implements /entity/panel route."""
# Get type of the record
type_ = Schema.get_type_by_short_name(request.type)
# Check if the selected type has the needed viewer and get its name (only viewer's label is provided)
handlers = HandlerDeclareBlockDecl.get_type_methods(type_, inherit=True).handlers
if (
handlers is not None
and handlers
and (found_viewers := [h.name for h in handlers if h.label == request.panel_id and h.type_ == "Viewer"])
):
viewer_name: str = found_viewers[0]
else:
raise Exception(f"Type {request.type} has no view with the name {request.panel_id}.")
# Deserialize key from string to object
serializer = StringSerializer()
key_obj = serializer.deserialize_key(data=request.key, type_=type_.get_key_type())
# Get database from the current context
db = Context.current().db
# Load record from the database
record = db.load_one(type_, key_obj, dataset=request.dataset)
if record is None:
raise RuntimeError(
f"Record with type {request.type} and key {request.key} is not found in dataset {request.dataset}."
)
# Call the viewer and get the result
viewer = getattr(record, viewer_name)
result_view = viewer()
# Apply legacy dict conventions
# TODO (Ina): Optimize speed using dacite or similar library
if isinstance(result_view, list):
view_dict = [PanelResponseUtil._get_view_dict(item) for item in result_view]
else:
view_dict = PanelResponseUtil._get_view_dict(result_view)
return {"ViewOf": view_dict}
@classmethod
def _get_view_dict(cls, view: Any) -> Dict[str, Any] | None:
"""Convert value to dict format."""
# Return None if view is None
if view is None:
return None
if isinstance(view, PlotView):
# Load plot for view if it is key
plot = Context.current().load_one(PlotKey, view.plot)
if plot is None:
raise RuntimeError(f"Not found plot for key {view.plot}.")
# Get view for plot and transform to ui format dict
return cls._get_view_dict(plot.get_view())
elif isinstance(view, KeyView):
# Load record for view
record = Context.current().load_one(type(view.key), view.key)
if record is None:
raise RuntimeError(f"Not found record for key {view.key}.")
# Return ui format dict dict of record
return cls._get_view_dict(record)
elif isinstance(view, PngView):
# Return ui format dict of binary data
return {
"Content": base64.b64encode(view.png_bytes).decode(),
"ContentType": "Png",
"_t": "BinaryContent",
}
elif isinstance(view, HtmlView):
# Return ui format dict of binary data
return {
"Content": base64.b64encode(view.html_bytes).decode(),
"ContentType": "Html",
"_t": "BinaryContent",
}
elif isinstance(view, PdfView):
# Return ui format dict of binary PDF view data
return {
"Content": base64.b64encode(view.pdf_bytes).decode(),
"ContentType": "Pdf",
"_t": "BinaryContent",
}
elif isinstance(view, Dag):
# Serialize Dag using ui serialization
view_dict = ui_serializer.serialize_data(view)
# Set _t with legacy Dag type name
view_dict["_t"] = "DAG"
# Return Dag view
return view_dict
elif isinstance(view, Script):
# Return script
view_dict: dict = to_legacy_dict(to_record_dict(view))
view_dict["Language"] = view_dict.pop("Language").capitalize()
return view_dict
elif isinstance(view, Dict):
# Return if is already dict
return view
else:
# TODO (Ina): Do not use a method from dataclasses
result_type = type(view)
if result_type.__name__.endswith("Key"):
view_dict = to_legacy_dict(dataclasses.asdict(view))
view_dict["_t"] = result_type.__name__
return view_dict
else:
return to_legacy_dict(to_record_dict(view))
Global variables
var ui_serializer
-
Ui serializer.
Classes
class PanelResponseUtil (**data: Any)
-
Response util for the /entity/panel route.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError
][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.self
is explicitly positional-only to allowself
as a field name.Expand source code
class PanelResponseUtil(BaseModel): """Response util for the /entity/panel route.""" view_of: PanelResponseData """Response data type for the /entity/panel route.""" @classmethod def get_content(cls, request: PanelRequest) -> Dict[str, PanelResponseData]: """Implements /entity/panel route.""" # Get type of the record type_ = Schema.get_type_by_short_name(request.type) # Check if the selected type has the needed viewer and get its name (only viewer's label is provided) handlers = HandlerDeclareBlockDecl.get_type_methods(type_, inherit=True).handlers if ( handlers is not None and handlers and (found_viewers := [h.name for h in handlers if h.label == request.panel_id and h.type_ == "Viewer"]) ): viewer_name: str = found_viewers[0] else: raise Exception(f"Type {request.type} has no view with the name {request.panel_id}.") # Deserialize key from string to object serializer = StringSerializer() key_obj = serializer.deserialize_key(data=request.key, type_=type_.get_key_type()) # Get database from the current context db = Context.current().db # Load record from the database record = db.load_one(type_, key_obj, dataset=request.dataset) if record is None: raise RuntimeError( f"Record with type {request.type} and key {request.key} is not found in dataset {request.dataset}." ) # Call the viewer and get the result viewer = getattr(record, viewer_name) result_view = viewer() # Apply legacy dict conventions # TODO (Ina): Optimize speed using dacite or similar library if isinstance(result_view, list): view_dict = [PanelResponseUtil._get_view_dict(item) for item in result_view] else: view_dict = PanelResponseUtil._get_view_dict(result_view) return {"ViewOf": view_dict} @classmethod def _get_view_dict(cls, view: Any) -> Dict[str, Any] | None: """Convert value to dict format.""" # Return None if view is None if view is None: return None if isinstance(view, PlotView): # Load plot for view if it is key plot = Context.current().load_one(PlotKey, view.plot) if plot is None: raise RuntimeError(f"Not found plot for key {view.plot}.") # Get view for plot and transform to ui format dict return cls._get_view_dict(plot.get_view()) elif isinstance(view, KeyView): # Load record for view record = Context.current().load_one(type(view.key), view.key) if record is None: raise RuntimeError(f"Not found record for key {view.key}.") # Return ui format dict dict of record return cls._get_view_dict(record) elif isinstance(view, PngView): # Return ui format dict of binary data return { "Content": base64.b64encode(view.png_bytes).decode(), "ContentType": "Png", "_t": "BinaryContent", } elif isinstance(view, HtmlView): # Return ui format dict of binary data return { "Content": base64.b64encode(view.html_bytes).decode(), "ContentType": "Html", "_t": "BinaryContent", } elif isinstance(view, PdfView): # Return ui format dict of binary PDF view data return { "Content": base64.b64encode(view.pdf_bytes).decode(), "ContentType": "Pdf", "_t": "BinaryContent", } elif isinstance(view, Dag): # Serialize Dag using ui serialization view_dict = ui_serializer.serialize_data(view) # Set _t with legacy Dag type name view_dict["_t"] = "DAG" # Return Dag view return view_dict elif isinstance(view, Script): # Return script view_dict: dict = to_legacy_dict(to_record_dict(view)) view_dict["Language"] = view_dict.pop("Language").capitalize() return view_dict elif isinstance(view, Dict): # Return if is already dict return view else: # TODO (Ina): Do not use a method from dataclasses result_type = type(view) if result_type.__name__.endswith("Key"): view_dict = to_legacy_dict(dataclasses.asdict(view)) view_dict["_t"] = result_type.__name__ return view_dict else: return to_legacy_dict(to_record_dict(view))
Ancestors
- pydantic.main.BaseModel
Class variables
var model_computed_fields
var model_config
var model_fields
var view_of
-
Response data type for the /entity/panel route.
Static methods
def get_content(request: PanelRequest) -> Dict[str, Union[Dict[str, Any], List[Dict[str, Any]], ForwardRef(None)]]
-
Implements /entity/panel route.