import json import logging from fasthtml.components import Script, Div from myfasthtml.core.dbmanager import DbObject from myfasthtml.core.instances import MultipleInstance logger = logging.getLogger("VisNetwork") class VisNetworkState(DbObject): def __init__(self, owner): super().__init__(owner) with self.initializing(): # persisted in DB self.nodes: list = [] self.edges: list = [] self.options: dict = { "autoResize": True, "interaction": { "dragNodes": True, "zoomView": True, "dragView": True, }, "physics": {"enabled": True} } class VisNetwork(MultipleInstance): def __init__(self, parent, _id=None, nodes=None, edges=None, options=None): super().__init__(parent, _id=_id) logger.debug(f"VisNetwork created with id: {self._id}") self._state = VisNetworkState(self) self._update_state(nodes, edges, options) def _update_state(self, nodes, edges, options): logger.debug(f"Updating VisNetwork state with {nodes=}, {edges=}, {options=}") if not nodes and not edges and not options: return state = self._state.copy() if nodes is not None: state.nodes = nodes if edges is not None: state.edges = edges if options is not None: state.options = options self._state.update(state) def add_to_options(self, **kwargs): logger.debug(f"add_to_options: {kwargs=}") new_options = self._state.options.copy() | kwargs self._update_state(None, None, new_options) return self def render(self): # Serialize nodes and edges to JSON # This preserves all properties (color, shape, size, etc.) that are present js_nodes = ",\n ".join( json.dumps(node) for node in self._state.nodes ) js_edges = ",\n ".join( json.dumps(edge) for edge in self._state.edges ) # Convert Python options to JS js_options = json.dumps(self._state.options, indent=2) return ( Div( id=self._id, cls="mf-vis", ), # The script initializing Vis.js Script(f""" (function() {{ const container = document.getElementById("{self._id}"); const nodes = new vis.DataSet([ {js_nodes} ]); const edges = new vis.DataSet([ {js_edges} ]); const data = {{ nodes: nodes, edges: edges }}; const options = {js_options}; const network = new vis.Network(container, data, options); }})(); """) ) def __ft__(self): return self.render()