Enhancing InstancesDebugger.py. I can display properties

This commit is contained in:
2025-12-02 22:09:37 +01:00
parent 378775cdf9
commit e96ac5ddfd
7 changed files with 82 additions and 22 deletions

View File

@@ -1,6 +1,7 @@
from myfasthtml.controls.Panel import Panel from myfasthtml.controls.Panel import Panel
from myfasthtml.controls.Properties import Properties
from myfasthtml.controls.VisNetwork import VisNetwork from myfasthtml.controls.VisNetwork import VisNetwork
from myfasthtml.core.commands import LambdaCommand from myfasthtml.core.commands import Command
from myfasthtml.core.instances import SingleInstance, InstancesManager from myfasthtml.core.instances import SingleInstance, InstancesManager
from myfasthtml.core.network_utils import from_parent_child_list from myfasthtml.core.network_utils import from_parent_child_list
@@ -9,13 +10,23 @@ class InstancesDebugger(SingleInstance):
def __init__(self, parent, _id=None): def __init__(self, parent, _id=None):
super().__init__(parent, _id=_id) super().__init__(parent, _id=_id)
self._panel = Panel(self, _id="-panel") self._panel = Panel(self, _id="-panel")
self._command = Command("ShowInstance",
"Display selected Instance",
self.on_network_event).htmx(target=f"#{self._panel.get_id()}_r")
def render(self): def render(self):
nodes, edges = self._get_nodes_and_edges() nodes, edges = self._get_nodes_and_edges()
c = LambdaCommand(lambda event_data: print("received", event_data)) vis_network = VisNetwork(self, nodes=nodes, edges=edges, _id="-vis", events_handlers={"select_node": self._command})
vis_network = VisNetwork(self, nodes=nodes, edges=edges, _id="-vis", events_handlers={"select_node": c})
return self._panel.set_main(vis_network) return self._panel.set_main(vis_network)
def on_network_event(self, event_data: dict):
session, instance_id = event_data["nodes"][0].split("#")
properties = {"Id": "_id", "Parent Id": "_parent._id"}
return self._panel.set_right(Properties(self,
InstancesManager.get(session, instance_id),
properties,
_id="-properties"))
def _get_nodes_and_edges(self): def _get_nodes_and_edges(self):
instances = self._get_instances() instances = self._get_instances()
nodes, edges = from_parent_child_list( nodes, edges = from_parent_child_list(

View File

@@ -46,6 +46,7 @@ class Panel(MultipleInstance):
sides, and adjust the width of the sides dynamically. The class also handles rendering sides, and adjust the width of the sides dynamically. The class also handles rendering
the panel with appropriate HTML elements and JavaScript for interactivity. the panel with appropriate HTML elements and JavaScript for interactivity.
""" """
def __init__(self, parent, conf=None, _id=None): def __init__(self, parent, conf=None, _id=None):
super().__init__(parent, _id=_id) super().__init__(parent, _id=_id)
self.conf = conf or PanelConf() self.conf = conf or PanelConf()
@@ -66,35 +67,35 @@ class Panel(MultipleInstance):
def set_right(self, right): def set_right(self, right):
self._right = right self._right = right
return self return Div(self._right, id=f"{self._id}_r")
def set_left(self, left): def set_left(self, left):
self._left = left self._left = left
return self return Div(self._left, id=f"{self._id}_l")
def _mk_right(self): def _mk_right(self):
if not self.conf.right: if not self.conf.right:
return None return None
resizer = Div( resizer = Div(
cls="mf-resizer mf-resizer-right", cls="mf-resizer mf-resizer-right",
data_command_id=self.commands.update_side_width("right").id, data_command_id=self.commands.update_side_width("right").id,
data_side="right" data_side="right"
) )
return Div(resizer, self._right, cls="mf-panel-right") return Div(resizer, Div(self._right, id=f"{self._id}_r"), cls="mf-panel-right")
def _mk_left(self): def _mk_left(self):
if not self.conf.left: if not self.conf.left:
return None return None
resizer = Div( resizer = Div(
cls="mf-resizer mf-resizer-left", cls="mf-resizer mf-resizer-left",
data_command_id=self.commands.update_side_width("left").id, data_command_id=self.commands.update_side_width("left").id,
data_side="left" data_side="left"
) )
return Div(self._left, resizer, cls="mf-panel-left") return Div(Div(self._left, id=f"{self._id}_l"), resizer, cls="mf-panel-left")
def render(self): def render(self):
return Div( return Div(

View File

@@ -0,0 +1,44 @@
from fasthtml.components import Div
from myutils.Expando import Expando
from myfasthtml.core.instances import MultipleInstance
class Properties(MultipleInstance):
def __init__(self, parent, obj=None, properties: dict = None, _id=None):
super().__init__(parent, _id=_id)
self.obj = obj
self.properties = properties or self._get_default_properties(obj)
self.expando = self._create_expando()
def set_obj(self, obj, properties: list[str] = None):
self.obj = obj
self.properties = properties or self._get_default_properties(obj)
def render(self):
return Div(
*[Div(k, ":", v) for k, v in self.expando.as_dict().items()],
id=self._id,
)
def _create_expando(self):
res = {}
for attr_name, mapping in self.properties.items():
attrs_path = mapping.split(".")
current = self.obj
for attr in attrs_path:
if hasattr(current, attr):
current = getattr(current, attr)
else:
res[attr_name] = None
break
res[attr_name] = current
return Expando(res)
@staticmethod
def _get_default_properties(obj):
return {k: k for k, v in dir(obj) if not k.startswith("_")} if obj else {}
def __ft__(self):
return self.render()

View File

@@ -101,13 +101,15 @@ class VisNetwork(MultipleInstance):
event_handlers_js += f""" event_handlers_js += f"""
network.on('{vis_event_name}', function(params) {{ network.on('{vis_event_name}', function(params) {{
const event_data = {{ const event_data = {{
event_name: '{event_name}',
nodes: params.nodes, nodes: params.nodes,
edges: params.edges, edges: params.edges,
pointer: params.pointer pointer: params.pointer
}}; }};
htmx.ajax('POST', '{command_htmx_options['url']}', {{ htmx.ajax('POST', '{command_htmx_options['url']}', {{
values: {{event_data: JSON.stringify(event_data)}}, values: {{event_data: JSON.stringify(event_data)}},
swap: 'none' target: '{command_htmx_options['target']}',
swap: '{command_htmx_options['swap']}'
}}); }});
}}); }});
""" """

View File

@@ -1,4 +1,5 @@
import inspect import inspect
import json
import uuid import uuid
from typing import Optional from typing import Optional
@@ -98,14 +99,12 @@ class BaseCommand:
return f"{ROUTE_ROOT}{Routes.Commands}?c_id={self.id}" return f"{ROUTE_ROOT}{Routes.Commands}?c_id={self.id}"
def ajax_htmx_options(self): def ajax_htmx_options(self):
res = {"url": self.url} return {
if "hx-target" in self._htmx_extra: "url": self.url,
res["target"] = self._htmx_extra["hx-target"] "target": self._htmx_extra.get("hx-target", "this"),
if "hx-swap" in self._htmx_extra: "swap": self._htmx_extra.get("hx-swap", "outerHTML"),
res["swap"] = self._htmx_extra["hx-swap"] "values": {}
res["values"] = {} }
return res
def get_ft(self): def get_ft(self):
return self._ft return self._ft
@@ -151,6 +150,8 @@ class Command(BaseCommand):
return float(value) return float(value)
elif param.annotation == list: elif param.annotation == list:
return value.split(",") return value.split(",")
elif param.annotation == dict:
return json.loads(value)
return value return value
def ajax_htmx_options(self): def ajax_htmx_options(self):

View File

@@ -84,7 +84,7 @@ class BaseInstance:
return self._prefix return self._prefix
def get_full_id(self) -> str: def get_full_id(self) -> str:
return f"{InstancesManager.get_session_id(self._session)}-{self._id}" return f"{InstancesManager.get_session_id(self._session)}#{self._id}"
def get_full_parent_id(self) -> Optional[str]: def get_full_parent_id(self) -> Optional[str]:
parent = self.get_parent() parent = self.get_parent()

View File

@@ -3,6 +3,7 @@ from collections.abc import Callable
ROOT_COLOR = "#ff9999" ROOT_COLOR = "#ff9999"
GHOST_COLOR = "#cccccc" GHOST_COLOR = "#cccccc"
def from_nested_dict(trees: list[dict]) -> tuple[list, list]: def from_nested_dict(trees: list[dict]) -> tuple[list, list]:
""" """
Convert a list of nested dictionaries to vis.js nodes and edges format. Convert a list of nested dictionaries to vis.js nodes and edges format.