WeakNode and Nested WeakNode (Interactive)
This notebook shows how to build Document -> Section -> Page hierarchies with WeakNode, how to inspect the graph state after each operation, and how to delete nodes with different strategies.
It also includes an interactive section where you choose which node to add or delete.
[ ]:
import importlib.util
# Comprovació de presència del paquet
package_to_check = 'drm'
spec = importlib.util.find_spec(package_to_check)
if spec is None:
print(f'⚠️ {package_to_check} no està instal·lat. Iniciant instal·lació...')
%pip install -q --upgrade drm-tools
print("✅ Instal·lació completada. L'estat del kernel PODRIA requerir un reinici.")
else:
print(f'✅ {package_to_check} ja està present al sistema. Saltant instal·lació.')
Overview
The notebook is organised as a step-by-step walkthrough followed by a small interactive control panel.
[ ]:
import os
import tempfile
import uuid
try:
from drm import NetworkXGraph, Node, WeakNode
except ImportError:
# Fallback for installs where WeakNode is not re-exported from drm.__init__
from drm import NetworkXGraph
from drm.base import Node, WeakNode
persistence_path = os.path.join(tempfile.gettempdir(), f"drm_tutorial_weaknodes_{uuid.uuid4().hex}.pkl")
graph = NetworkXGraph(persistence_path=persistence_path)
registry = {}
def key_for_document(doc_id):
return f"doc:{doc_id}"
def key_for_section(doc_id, section_id):
return f"section:{doc_id}:{section_id}"
def key_for_page(doc_id, section_id, page_id):
return f"page:{doc_id}:{section_id}:{page_id}"
def show_state(title="State"):
print(f"\n=== {title} ===")
print("nodes:", graph.get_nodes())
print("edges:", graph.get_edges())
debug = graph.debug()
print("debug nodes:", debug.get("nodes", []))
print("debug edges:", debug.get("edges", []))
def add_document(doc_id):
k = key_for_document(doc_id)
node = Node(pk={"doc": doc_id}, main_label="Document")
graph.insertNode(node, replace=True)
registry[k] = node
return k
def add_section(doc_id, section_id):
parent_key = key_for_document(doc_id)
if parent_key not in registry:
add_document(doc_id)
parent = registry[parent_key]
k = key_for_section(doc_id, section_id)
node = WeakNode(parent=parent, pk={"section": section_id}, main_label="Section")
graph.insertNode(node, insert_parent=True, replace=True)
registry[k] = node
return k
def add_page(doc_id, section_id, page_id):
section_key = key_for_section(doc_id, section_id)
if section_key not in registry:
add_section(doc_id, section_id)
parent = registry[section_key]
k = key_for_page(doc_id, section_id, page_id)
node = WeakNode(parent=parent, pk={"page": page_id}, main_label="Page")
graph.insertNode(node, insert_parent=True, replace=True)
registry[k] = node
return k
def delete_key(k, detach=True, propagation=False, on_delete="cascade"):
node = registry.get(k)
if node is None:
raise KeyError(f"Unknown key: {k}")
graph.deleteNode(node, detach=detach, propagation=propagation, on_delete=on_delete)
registry.pop(k, None)
print("Graph initialized at:", persistence_path)
Flux bàsic (pas a pas)
[ ]:
add_document("DOC-001")
show_state("After adding Document")
add_section("DOC-001", 1)
show_state("After adding Section as WeakNode")
add_page("DOC-001", 1, 1)
show_state("After adding Page as WeakNode of WeakNode")
[ ]:
delete_key("section:DOC-001:1", detach=True, propagation=False, on_delete="cascade")
show_state("After deleting Section with ON DELETE CASCADE")
Secció interactiva (tria operació i node)
Si tens ipywidgets disponible, pots afegir/esborrar nodes manualment.
[ ]:
from IPython.display import display, clear_output
import ipywidgets as widgets
doc_w = widgets.Text(value="DOC-002", description="doc")
section_w = widgets.IntText(value=1, description="section")
page_w = widgets.IntText(value=1, description="page")
delete_key_w = widgets.Dropdown(options=[], description="delete")
detach_w = widgets.Checkbox(value=True, description="detach")
prop_w = widgets.Checkbox(value=False, description="propagation")
on_delete_w = widgets.Dropdown(options=["cascade", "restrict", "set_null"], value="cascade", description="on_delete")
out = widgets.Output()
def refresh_keys(*_):
opts = sorted(registry.keys())
delete_key_w.options = opts if opts else [""]
def act_add_doc(_):
with out:
clear_output()
k = add_document(doc_w.value)
print("added", k)
show_state("Interactive add document")
refresh_keys()
def act_add_section(_):
with out:
clear_output()
k = add_section(doc_w.value, section_w.value)
print("added", k)
show_state("Interactive add section")
refresh_keys()
def act_add_page(_):
with out:
clear_output()
k = add_page(doc_w.value, section_w.value, page_w.value)
print("added", k)
show_state("Interactive add page")
refresh_keys()
def act_delete(_):
with out:
clear_output()
k = delete_key_w.value
if not k:
print("No node selected")
return
try:
delete_key(k, detach=detach_w.value, propagation=prop_w.value, on_delete=on_delete_w.value)
print("deleted", k)
show_state("Interactive delete")
except Exception as err:
print("delete error:", err)
refresh_keys()
btn_doc = widgets.Button(description="Add Document")
btn_sec = widgets.Button(description="Add Section")
btn_page = widgets.Button(description="Add Page")
btn_del = widgets.Button(description="Delete Selected", button_style="danger")
btn_doc.on_click(act_add_doc)
btn_sec.on_click(act_add_section)
btn_page.on_click(act_add_page)
btn_del.on_click(act_delete)
refresh_keys()
display(widgets.VBox([
widgets.HBox([doc_w, section_w, page_w]),
widgets.HBox([btn_doc, btn_sec, btn_page]),
widgets.HBox([delete_key_w, detach_w, prop_w, on_delete_w, btn_del]),
out,
]))
Cleanup
[ ]:
graph.close()
try:
os.remove(persistence_path)
except FileNotFoundError:
pass
print("Closed graph and removed temp persistence file")