costruzione grafo

This commit is contained in:
Nicola Leonardi 2026-06-20 18:37:57 +02:00
parent 59dab45944
commit 3966af1510
14 changed files with 4155 additions and 3 deletions

View File

@ -0,0 +1,690 @@
"""
Architettura dello script
Il pipeline ha 6 fasi ben separate.
Fase 1 Playwright snapshot (_flatten_playwright_snapshot): estrae l'albero annidato restituito da page.accessibility.snapshot(interesting_only=False) e lo appiattisce in un dizionario. Ogni nodo porta role, name, stato ARIA (checked, expanded, disabled, required, ecc.) e un riferimento al parent per ricostruire la struttura.
Fase 2 CDP via cdp_use (extract_and_build): lancia Chromium con --remote-debugging-port=9222, poi connette CDPClient al WebSocket del target page. Chiama Accessibility.enable Accessibility.getFullAXTree Accessibility.disable. Questo restituisce tutti i nodi AX inclusi quelli ignored, con parentId, childIds, backendDOMNodeId e le properties ARIA estese.
Fase 3 Build grafo (build_graph): tre pass distinti. Il primo aggiunge tutti i nodi CDP con attributi puliti (_node_to_attrs). Il secondo aggiunge gli archi strutturali parentchild. Il terzo aggiunge archi ARIA relazionali (labelledby, describedby, controls, owns, flowto, activedescendant) risolti tramite backendDOMNodeId.
Fase 4 Merge (merge_playwright_data): riconcilia i nodi Playwright con i nodi CDP per (role, name) e arricchisce gli attributi del grafo utile perché Playwright espone alcuni stati che CDP non restituisce direttamente.
Fase 5 Export: supporta sia .graphml (portabile, compatibile con Gephi/yEd) che .json node-link (compatibile con D3.js e altri tool di visualizzazione).
"""
"""Riferimenti utili
browser_use:
site-packages\\browser_use\dom\\views.py #per aria-attributes
"""
"""
environmnet to use: accessibility_graph
ax_tree_to_graph.py
===================
Extract the accessibility tree of a web page and transform it into a
NetworkX directed graph.
Two extraction strategies are used and merged:
1. CDP (cdp_use) low-level Chrome DevTools Protocol via
Accessibility.getFullAXTree, which exposes every node
including ignored ones, parentId / childIds,
backendDOMNodeId, and all ARIA properties.
2. Playwright high-level snapshot via page.accessibility.snapshot(),
used to enrich CDP nodes with computed state attributes.
The final graph uses CDP node-ids as node keys. Playwright data is merged
as additional attributes where a matching node is found.
Usage
-----
python ax_tree_to_graph.py <url> [--output graph.graphml] [--draw] [--no-ignored]
python ax_tree_to_graph.py https://www.regione.toscana.it/regione/amministrazione-trasparente/bandi-di-concorso --output graph.json --draw
python ax_tree_to_graph.py https://www.regione.toscana.it/regione/amministrazione-trasparente/bandi-di-concorso --output graph_ignored.json --draw --no-ignored #per ignorare quelli nodi che hanno ignored=true
Requirements
------------
pip install playwright cdp-use networkx matplotlib
playwright install chromium
"""
import argparse
import asyncio
import json
import socket
import sys
from typing import Any, Optional
import networkx as nx
from playwright.async_api import async_playwright
from cdp_use import CDPClient
# ─────────────────────────────────────────────────────────────────────────────
# Helpers
# ─────────────────────────────────────────────────────────────────────────────
def _find_free_port() -> int:
"""Return an available TCP port on localhost."""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("", 0))
return s.getsockname()[1]
# ─────────────────────────────────────────────────────────────────────────────
# 1. Playwright snapshot → flat lookup dict {(role, name) -> attrs}
# ─────────────────────────────────────────────────────────────────────────────
import re
import yaml
from typing import Optional, Union, List, Dict, Any
import re
import yaml
from typing import Optional, Any
import re
import yaml
from typing import Optional, Any
def _flatten_playwright_snapshot(
yaml_str: str,
result: Optional[dict] = None,
) -> dict:
"""
Parses Playwright's YAML ARIA snapshot string and flattens it into a dict
keyed by (role, name) for later merging with CDP nodes.
"""
if result is None:
result = {}
try:
parsed_data = yaml.safe_load(yaml_str)
except Exception as e:
print(f" [!] Failed to parse ARIA snapshot YAML: {e}")
return result
node_regex = re.compile(r'^([a-zA-Z0-9_-]+)(?:\s+"([^"]*)")?(?:\s+\[(.*)\])?$')
def _process_node(node: Any, depth: int = 0):
if isinstance(node, list):
for item in node:
_process_node(item, depth)
elif isinstance(node, dict):
for key, value in node.items():
_parse_single_node(key, value, depth)
elif isinstance(node, str):
_parse_single_node(node, None, depth)
def _parse_single_node(node_key: str, node_value: Any, depth: int):
if node_key == "box":
return
match = node_regex.match(node_key.strip())
if not match:
return
role = (match.group(1) or "unknown").lower()
name = (match.group(2) or "").strip()
attr_string = match.group(3) or ""
if role == "text" and not name:
name = node_key.replace("text ", "").strip('" ')
key = (role, name)
if key not in result:
attrs = {
"pw_role": role,
"pw_name": name,
"pw_depth": depth, #represents the nesting level of that element within the page's accessibility tree structure. The accessibility depth (pw_depth) is usually shallower than the actual DOM level because it skips all the purely visual wrapper elements
#"pw_level": #gestito in maniera custom sotto #pw_level represents the hierarchical structural level of an element—most commonly used for headings, lists, and tree grids.
#It directly maps to the standard HTML heading levels (<h1> through <h6>) or explicit aria-level attributes used on elements.
#This is a semantic property coming directly from the browser's accessibility layer. pw_depth: This is a structural
}
# ── 1. Clean & extract box details ────────────────────────────
if "box=" in attr_string:
box_match = re.search(r'box=\[?(-?\d+),(-?\d+),(-?\d+),(-?\d+)\]?', attr_string)
if box_match:
attrs["pw_box"] = [int(x) for x in box_match.groups()]
attr_string = re.sub(r',?\s*box=\[?[\d,,-]+\]?', '', attr_string).strip()
# ── 2. FIX: Clean & extract level details safely ──────────────
if "level=" in attr_string:
level_match = re.search(r'level=(\d+)', attr_string)
if level_match:
attrs["pw_level"] = int(level_match.group(1))
# Clean out the level substring so the comma loop doesn't get corrupted brackets
attr_string = re.sub(r',?\s*level=\d+\]?\s*\[?', '', attr_string).strip()
# ── 3. Parse remaining standard inline attributes ─────────────
if attr_string:
# Clean up residual broken brackets before splitting
attr_string = attr_string.replace('[', '').replace(']', '').strip()
for pair in attr_string.split(','):
pair = pair.strip()
if not pair:
continue
if '=' in pair:
k, v = pair.split('=', 1)
attrs[f"pw_{k.strip()}"] = v.strip().strip('"')
else:
attrs[f"pw_{pair}"] = True
# ── 4. Extract nested structural sub-nodes ────────────────────
if isinstance(node_value, dict):
if "box" in node_value:
attrs["pw_box"] = node_value["box"]
for k, v in node_value.items():
if k != "box" and not node_regex.match(k.strip()):
attrs[f"pw_{k}"] = v
result[key] = {k: v for k, v in attrs.items() if v is not None and v != ""}
# ── 5. Handle child hierarchies ───────────────────────────────────
if isinstance(node_value, list):
_process_node(node_value, depth + 1)
elif isinstance(node_value, dict):
for k, v in node_value.items():
if k != "box" and node_regex.match(k.strip()):
_parse_single_node(k, v, depth + 1)
_process_node(parsed_data)
return result
# ─────────────────────────────────────────────────────────────────────────────
# 2. CDP AXNode helpers
# ─────────────────────────────────────────────────────────────────────────────
def _unwrap_ax_value(ax_value: Optional[dict]) -> Any:
"""
Unwrap a CDP AXValue dict { type, value, relatedNodes?, }
to a plain Python scalar.
"""
if ax_value is None:
return None
raw = ax_value.get("value")
typ = ax_value.get("type", "")
if typ == "boolean":
return bool(raw)
if typ in ("integer", "number"):
try:
return float(raw)
except (TypeError, ValueError):
return raw
if typ=="nodeList": #typical for labelledby properties
# Return a list of backendDOMNodeId for related nodes
#print(f"[*] Unwrapping nodeList: {ax_value}")
return [n.get("idref") for n in ax_value.get("relatedNodes", []) if n.get("backendDOMNodeId") is not None]
if isinstance(raw, dict):
return raw.get("value") or str(raw)
return raw
def _node_to_attrs(node: dict) -> dict:
"""Convert a raw CDP AXNode dict into clean, flat node attributes."""
attrs: dict = {
"node_id": node.get("nodeId", ""),
"ignored": node.get("ignored", False),
"role": _unwrap_ax_value(node.get("role")),
"chrome_role": _unwrap_ax_value(node.get("chromeRole")),
"name": _unwrap_ax_value(node.get("name")),
"description": _unwrap_ax_value(node.get("description")),
"value": _unwrap_ax_value(node.get("value")),
"backend_dom_node_id": node.get("backendDOMNodeId"),
"frame_id": node.get("frameId"),
"type": _unwrap_ax_value(node.get("role")),# already use for role
}
# Flatten all ARIA properties (focusable, focused, disabled, level, …)
#NB aria-hidden property is missing, because it is not returned directly from CDP nor playwright. Probably can be inferred with some heuristic
for prop in node.get("properties") or []: # manage properties attributes
pname = prop.get("name", "")
pval = _unwrap_ax_value(prop.get("value"))
if pname:
attrs[f"aria_{pname}"] = pval
# Collect ignore reasons as a readable string
reasons = [
# _unwrap_ax_value(r.get("value"))
r.get("name")
for r in node.get("ignoredReasons") or []
]
filtered = [str(r) for r in reasons if r is not None]
if filtered:
attrs["ignored_reasons"] = "; ".join(filtered)
return {k: v for k, v in attrs.items() if v is not None and v != ""}
# ─────────────────────────────────────────────────────────────────────────────
# 3. Build NetworkX graph from CDP nodes
# ─────────────────────────────────────────────────────────────────────────────
def build_graph(cdp_nodes: list[dict]) -> nx.DiGraph:
"""
Build a directed graph from the CDP AXNode list.
Nodes : one per AXNode, keyed by nodeId string.
Edges : parent child (relation="child")
node related_node (relation=aria_property_name,
e.g. "labelledby", "controls")
"""
G = nx.DiGraph()
id_to_raw: dict[str, dict] = {}
# ── pass 1: add all nodes ─────────────────────────────────────────────
for raw in cdp_nodes:
nid = raw.get("nodeId", "")
if not nid:
continue
G.add_node(nid, **_node_to_attrs(raw))
id_to_raw[nid] = raw
# ── pass 2: structural edges (parent → child) ─────────────────────────
edge_attributes_child = {
"relation": "hasChild",
"type": "hasChild"
}
edge_attributes_parent = {
"relation": "hasParent",
"type": "hasParent"
}
"""for nid, raw in id_to_raw.items():
parent_id = raw.get("parentId")
if parent_id and parent_id in G and not G.has_edge(parent_id, nid):
G.add_edge(parent_id, nid, **edge_attributes)
for child_id in raw.get("childIds") or []:
if child_id in G and not G.has_edge(nid, child_id):
G.add_edge(nid, child_id, **edge_attributes)"""
for nid, raw in id_to_raw.items():
parent_id = raw.get("parentId")
# Direct link to parent: Flow goes UP from child to parent
if parent_id and parent_id in G:
if not G.has_edge(nid, parent_id):
G.add_edge(nid, parent_id, **edge_attributes_parent)
# Optional: Add the downward link if you want bidirectional navigation
#if not G.has_edge(parent_id, nid):
# G.add_edge(parent_id, nid, **edge_attributes_child)
# Direct link to children: Flow goes DOWN from parent to child
for child_id in raw.get("childIds") or []:
if child_id in G:
if not G.has_edge(nid, child_id):
G.add_edge(nid, child_id, **edge_attributes_child)
# Optional: Add the upward link
#if not G.has_edge(child_id, nid):
# G.add_edge(child_id, nid, **edge_attributes_parent)
# ── pass 3: ARIA relationship edges ───────────────────────────────────
# these links are built from node properties setted before (from "node properties" attributes). In reality they are built from id_to_raw but they follow the same elaboration
# these link has superior importance respect to parent-child relationship because they represent a semantic relationship. ths is why they override he previous
# because they are both node attributes and edge attributes, mayybe are redundant. TBM
ARIA_REL_PROPS = { #aria-describedby has the name describedby and so on...
#Labeling and Describing. These attributes form a relationship between an element and the text that identifies or explains it.
'describedby', #'aria-describedby' has the name 'describedby' in CDP properties
'labelledby',
'details',
#Controlling and Owning. These attributes define hierarchy, control, and structural relationships when elements aren't nested inside each other in the actual HTML code.
'controls',
'owns',
'activedescendant',
#Navigating and Ordering. These attributes help assistive technologies understand how to navigate between distinct elements or content blocks.
'flowto',
'colindex',
'rowindex'
}
backend_to_nid: dict[int, str] = {
raw.get("backendDOMNodeId"): nid
for nid, raw in id_to_raw.items()
if raw.get("backendDOMNodeId") is not None
}
for nid, raw in id_to_raw.items():
for prop in raw.get("properties") or []:
pname = prop.get("name", "")
if pname not in ARIA_REL_PROPS:
continue
else:
print(f" [*] Processing ARIA relation property: {pname}")
pval = prop.get("value", {})
print(f" [*] ARIA relation value: {pval}")
for rel_node in pval.get("relatedNodes", []):
bid = rel_node.get("backendDOMNodeId")
target = backend_to_nid.get(bid)
if not target:
print(f" [!] Warning: No target node found for ARIA relation {pname} with backendDOMNodeId {bid}")
if G.has_edge(nid, target):
print(f" [!] Warning: Edge already exists for {nid} --{pname}--> {target}, updating link")
#if target and not G.has_edge(nid, target):
if target : #ovverride! this link is more important than the parent-child relationship
print(f" [*] Adding ARIA edge: {nid} --{pname}--> {target}")
edge_attributes = {
"relation": pname,
"type": pname
}
G.add_edge(nid, target, **edge_attributes)
return G
# ─────────────────────────────────────────────────────────────────────────────
# 4. Merge Playwright data into the graph (best-effort by role+name)
# ─────────────────────────────────────────────────────────────────────────────
def merge_playwright_data(G: nx.DiGraph, pw_flat: dict) -> None:
"""
Match Playwright nodes to CDP nodes by (role, name) and enrich
graph node attributes with Playwright-specific computed states.
NB: only nodes are managed, not edges, since Playwright snapshot doesn't expose parent/child relationships.
"""
role_name_to_nid: dict[tuple, str] = {}
for nid, attrs in G.nodes(data=True):
role = str(attrs.get("role") or "").lower()
name = str(attrs.get("name") or "").strip()
key = (role, name)
if key not in role_name_to_nid:
role_name_to_nid[key] = nid
for (role, name), pw_attrs in pw_flat.items():
nid = role_name_to_nid.get((role, name))
if nid:
# Only add keys not already present from CDP
existing = G.nodes[nid]
for k, v in pw_attrs.items():
if k not in existing:
G.nodes[nid][k] = v
# ─────────────────────────────────────────────────────────────────────────────
# 5. Graph summary
# ─────────────────────────────────────────────────────────────────────────────
def print_graph_summary(G: nx.DiGraph) -> None:
print(f"\n{'='*60}")
print(" Accessibility Tree Graph Summary")
print(f"{'='*60}")
print(f" Nodes (AX nodes) : {G.number_of_nodes()}")
print(f" Edges : {G.number_of_edges()}")
ignored = sum(1 for _, a in G.nodes(data=True) if a.get("ignored"))
print(f" Ignored nodes : {ignored}")
roles: dict[str, int] = {}
for _, attrs in G.nodes(data=True):
r = str(attrs.get("role") or "none")
roles[r] = roles.get(r, 0) + 1
print("\n Role distribution (top 20):")
for role, cnt in sorted(roles.items(), key=lambda x: -x[1])[:20]:
print(f" {role:<30} {cnt}")
edge_types: dict[str, int] = {}
for _, _, a in G.edges(data=True):
rel = a.get("relation", "unknown")
edge_types[rel] = edge_types.get(rel, 0) + 1
print("\n Edge types:")
for rel, cnt in sorted(edge_types.items(), key=lambda x: -x[1]):
print(f" {rel:<30} {cnt}")
components = nx.number_connected_components(G.to_undirected())
dag = nx.is_directed_acyclic_graph(G)
print(f"\n Connected components: {components}")
print(f" DAG : {'yes' if dag else 'no (cycles detected)'}")
print(f"{'='*60}\n")
# ─────────────────────────────────────────────────────────────────────────────
# 6. Export
# ─────────────────────────────────────────────────────────────────────────────
def save_graph(G: nx.DiGraph, path: str) -> None:
if path.endswith(".json"):
data = nx.node_link_data(G)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, default=str)
print(f"[*] Saved → {path} (node-link JSON)")
else:
# GraphML requires all attribute values to be strings
G_exp = G.copy()
for _, attrs in G_exp.nodes(data=True):
for k in list(attrs):
attrs[k] = str(attrs[k])
for _, _, attrs in G_exp.edges(data=True):
for k in list(attrs):
attrs[k] = str(attrs[k])
nx.write_graphml(G_exp, path)
print(f"[*] Saved → {path} (GraphML)")
def draw_graph(G: nx.DiGraph, max_nodes: int = 80, out: str = "ax_graph.png") -> None:
try:
import matplotlib.pyplot as plt
except ImportError:
print("[!] matplotlib not installed — skipping draw")
return
if G.number_of_nodes() > max_nodes:
print(f"[*] Subgraph: top {max_nodes} nodes by degree")
top = sorted(G.nodes(), key=lambda n: G.degree(n), reverse=True)[:max_nodes]
G = G.subgraph(top).copy()
ROLE_COLORS = {
"WebArea": "#4A90D9", "RootWebArea": "#4A90D9",
"heading": "#F57C00", "link": "#388E3C",
"button": "#7B1FA2", "img": "#C62828",
"image": "#C62828", "textbox": "#1565C0",
"navigation": "#00838F", "main": "#558B2F",
"banner": "#AD1457", "contentinfo": "#6D4C41",
"list": "#0288D1", "listitem": "#0288D1",
"form": "#E64A19", "checkbox": "#6A1B9A",
"combobox": "#283593", "region": "#4527A0",
"generic": "#B0BEC5", "none": "#ECEFF1",
}
node_colors, labels = [], {}
for nid, attrs in G.nodes(data=True):
role = str(attrs.get("role") or attrs.get("pw_role") or "none")
node_colors.append(ROLE_COLORS.get(role, "#CFD8DC"))
name = str(attrs.get("name") or attrs.get("pw_name") or "")[:22]
labels[nid] = f"{role}\n{name}" if name else role
plt.figure(figsize=(22, 15))
try:
pos = nx.nx_agraph.graphviz_layout(G, prog="dot")
except Exception:
pos = nx.spring_layout(G, seed=42, k=2.5)
nx.draw_networkx(
G, pos=pos, labels=labels,
node_color=node_colors, node_size=700,
font_size=6, arrows=True,
edge_color="#90A4AE", width=0.8,
)
plt.title("Accessibility Tree Graph", fontsize=14)
plt.axis("off")
plt.tight_layout()
plt.savefig(out, dpi=150, bbox_inches="tight")
print(f"[*] Graph image saved → {out}")
# ─────────────────────────────────────────────────────────────────────────────
# 7. Main pipeline
# ─────────────────────────────────────────────────────────────────────────────
async def extract_and_build(url: str) -> nx.DiGraph:
"""
Full pipeline:
a) Pick a free TCP port for CDP remote debugging.
b) Launch Chromium (Playwright) with that port.
c) Navigate to the URL and wait for network idle.
d) Extract AX snapshot via page.accessibility.snapshot().
e) Extract full AX tree via CDP (cdp_use).
f) Build NetworkX graph; merge Playwright data.
"""
debug_port = _find_free_port()
print(f"[*] Using CDP debug port: {debug_port}")
async with async_playwright() as pw:
browser = await pw.chromium.launch(
headless=True,
args=[f"--remote-debugging-port={debug_port}"],
)
context = await browser.new_context(viewport={"width": 1920, "height": 1080})
page = await context.new_page()
print(f"[*] Navigating to: {url}")
await page.goto(url, wait_until="networkidle", timeout=30_000)
await page.wait_for_timeout(1000) # let dynamic content settle
# ── Step A: Playwright accessibility snapshot ─────────────────────
print("[*] Playwright: extracting accessibility snapshot …")
pw_snapshot_str: str = ""
try:
# Captures the new YAML-based ARIA snapshot including bounding box coordinates
# https://playwright.dev/python/docs/aria-snapshots
snapshot = await page.aria_snapshot(boxes=True,mode="ai")
if snapshot:
pw_snapshot_str = snapshot
# Since it's a YAML string, we can count lines or characters to log density
#print(f"yaml file: {pw_snapshot_str.strip().splitlines()}")
line_count = len(pw_snapshot_str.strip().splitlines())
print(f" → Playwright ARIA snapshot collected ({line_count} lines of YAML structure)")
pw_flat = _flatten_playwright_snapshot(snapshot)
else:
print(" [!] Snapshot returned None (page may be empty)")
except Exception as e:
print(f" [!] Playwright snapshot failed: {e}")
# ── Step B: CDP via cdp_use ───────────────────────────────────────
print("[*] CDP: connecting via cdp_use …")
cdp_nodes: list[dict] = []
try:
import urllib.request
with urllib.request.urlopen(
f"http://localhost:{debug_port}/json", timeout=5
) as resp:
targets = json.loads(resp.read())
page_targets = [
t for t in targets
if t.get("type") == "page" and "webSocketDebuggerUrl" in t
]
if not page_targets:
raise RuntimeError("No page target found in CDP /json endpoint")
ws_url = page_targets[0]["webSocketDebuggerUrl"]
print(f" → WebSocket: {ws_url}")
async with CDPClient(ws_url) as cdp:
await cdp.send_raw("Accessibility.enable")
result = await cdp.send_raw("Accessibility.getFullAXTree", {})
cdp_nodes = result.get("nodes", [])
await cdp.send_raw("Accessibility.disable")
print(f"{len(cdp_nodes)} CDP AX nodes collected")
except Exception as e:
print(f" [!] CDP extraction failed: {e}")
await browser.close()
# ── Build graph ───────────────────────────────────────────────────────
if cdp_nodes:
print("[*] Building graph from CDP nodes …")
G = build_graph(cdp_nodes)
if pw_flat:
print("[*] Merging Playwright attributes …")
merge_playwright_data(G, pw_flat)
elif pw_flat:
print("[!] CDP unavailable — building graph from Playwright snapshot only")
G = _build_graph_from_pw(pw_flat)
else:
raise RuntimeError("No accessibility data could be extracted.")
return G
def _build_graph_from_pw(pw_flat: dict) -> nx.DiGraph:
"""Fallback: build a simple graph from Playwright snapshot only."""
G = nx.DiGraph()
id_map: dict[tuple, str] = {}
for i, (key, attrs) in enumerate(pw_flat.items()):
nid = str(i)
id_map[key] = nid
G.add_node(nid, **attrs)
# Playwright flat dict doesn't carry parent references directly;
# reconstruct by depth ordering (parent = most recent node at depth-1)
depth_stack: dict[int, str] = {}
for i, (key, attrs) in enumerate(pw_flat.items()):
nid = str(i)
depth = attrs.get("pw_depth", 0)
if depth > 0 and (depth - 1) in depth_stack:
G.add_edge(depth_stack[depth - 1], nid, relation="pw_child")#static, has to be manged
depth_stack[depth] = nid
return G
# ─────────────────────────────────────────────────────────────────────────────
# 8. CLI
# ─────────────────────────────────────────────────────────────────────────────
def _parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(
description="Extract the accessibility tree of a web page → NetworkX graph"
)
p.add_argument("url", help="URL to analyse (http/https or file://)")
p.add_argument(
"--output", "-o", default="ax_graph.graphml",
help="Output path (.graphml or .json) [default: ax_graph.graphml]",
)
p.add_argument(
"--draw", action="store_true",
help="Render and save ax_graph.png",
)
p.add_argument(
"--no-ignored", action="store_true",
help="Remove ignored AX nodes before saving",
)
return p.parse_args()
async def main() -> None:
args = _parse_args()
G = await extract_and_build(args.url)
if args.no_ignored:
to_remove = [n for n, a in G.nodes(data=True) if a.get("ignored")]
G.remove_nodes_from(to_remove)
print(f"[*] Removed {len(to_remove)} ignored nodes")
print_graph_summary(G)
save_graph(G, args.output)
if args.draw:
draw_graph(G)
if __name__ == "__main__":
asyncio.run(main())

View File

@ -0,0 +1,24 @@
import json
import networkx as nx
from pyvis.network import Network
# 1. Load the Node-link JSON data
file_path = "graph.json" # Replace with your actual file path
with open(file_path, "r") as f:
data = json.load(f)
# 2. Reconstruct the NetworkX graph
G = nx.readwrite.json_graph.node_link_graph(data)
# 3. Initialize PyVis Network
# '100%' width/height makes it responsive. 'notebook=False' is for standalone scripts.
net = Network(height="750px", width="100%", bgcolor="#222222", font_color="white",filter_menu=True,cdn_resources="remote")
# 4. Import the NetworkX graph into PyVis
net.from_nx(G)
# 5. Enable the interactive physics/control UI panel (Optional, but great for tweaking layout)
net.show_buttons(filter_=["physics", "nodes", "edges"])
# 6. Save and open the interactive graph in your default browser
net.write_html("interactive_graph.html", open_browser=True)

View File

@ -0,0 +1,179 @@
#### To launch the script
# python KnowledgeGraph_manager.py --graph_name accessibility_graph --graph_type directed --from_graph ../graph.json --save_to_neo4j
import sys
import argparse
import json
from dependences.graph.neo4j_manager import Neo4jManager, neo4j_exception
from dependences.graph.graph_manager import (
GraphManager,
key_error_exception,
graph_exception,
)
from dotenv import load_dotenv, find_dotenv
from dependences.utils.utils import return_from_env_valid, logging_startup, Params
import logging
exception_msg = "Exception: %s"
def app_startup(use_neo4j=False):
default_savelog = return_from_env_valid("savelog", "False")
default_loglevel = return_from_env_valid("loglevel", "INFO")
default_logmode = return_from_env_valid("logmode", "a")
default_logpath = return_from_env_valid("logpath", "/my_kg_server_log.log")
directory_separator = return_from_env_valid("directory_separator", "/")
params = Params(
default_savelog,
default_loglevel,
default_logmode,
default_logpath,
directory_separator,
)
logging_startup(params)
default_persistence_path = return_from_env_valid(
"default_persistence_path", "persistence"
)
GRAPH_CONFIG = {"default_persistence_path": default_persistence_path}
graph_manager = GraphManager(GRAPH_CONFIG)
neo4j_manager = None
if use_neo4j:
neo4j_uri = return_from_env_valid("neo4j_uri", "bolt://localhost:7687")
neo4j_user = return_from_env_valid("neo4j_user", "neo4j")
neo4j_password = return_from_env_valid("neo4j_password", "neo4j")
neo4j_database = return_from_env_valid("neo4j_database", "neo4j")
NEO4J_CONFIG = {
"uri": neo4j_uri,
"user": neo4j_user,
"password": neo4j_password,
"database": neo4j_database,
}
neo4j_manager = Neo4jManager(NEO4J_CONFIG)
return graph_manager, neo4j_manager
def cli(sys_argv):
# Import environment variables
env_path = find_dotenv(filename="env.env")
if env_path == "":
print("env path not found: service starting with the default params values")
_ = load_dotenv(env_path) # read .env file
parser = argparse.ArgumentParser(description="Knowledge Graph Management")
graph_type_options = {"direct", "undirected"}
parser.add_argument(
"--graph_type",
type=str,
default="directed",
help=(f"Which graph type to buid. Options: {graph_type_options}."),
)
parser.add_argument(
"--graph_name",
type=str,
default="my_graphname",
help="the graph name to be created.",
)
parser.add_argument(
"--save_to_neo4j", action="store_true", help="save graph on Neo4j"
)
parser.add_argument(
"--from_graph",
type=str,
default="",
help="start from a pre-existing graph. Path and name has to be provided.",
)
args = parser.parse_args(sys_argv)
graph_manager, neo4j_manager = app_startup(use_neo4j=args.save_to_neo4j)
logging.info("kg manager started with cli args:%s", args)
if 1:#args.task == "populate_graph":
try:
if args.from_graph != "":
logging.info("populate graph from a pre-existing one")
start_from_empty_graph = False
ini_graph = graph_manager.load_graph_from_file(args.from_graph)
else:
logging.info("populate graph from scratch")
start_from_empty_graph = True
ini_graph = None
graph = ini_graph
graph_manager.graphs[args.graph_name] = graph
try:
graph_manager.persist_graph_to_file(
graph_name=args.graph_name,
file_name=graph_manager.default_persistence_path
+ "/"
+ args.graph_name
+ ".json",
)
except Exception as e:
logging.error(exception_msg + " %s", e)
if args.save_to_neo4j:
logging.info("saving graph to neo4j...")
neo4j_manager.delete_graph_from_neo4j() # because neo4j support only one graph delete is mandatory
neo4j_manager.save_graph_to_neo4j(graph)
logging.info("graph saved to neo4j")
except key_error_exception as e:
logging.error(e)
except graph_exception as e:
logging.error(e)
except neo4j_exception as e:
logging.error(e)
except Exception as e:
logging.error(exception_msg + " %s", e)
else:
logging.info("graph %s correctly created", args.graph_name)
else:
logging.warning("The task:%s is not managed", args.task)
raise ValueError(
f"{args.task} is not a valid --args.exercise_solution option. Options: {task_type_options}"
)
if __name__ == "__main__":
cli(sys.argv[1:])

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,292 @@
import networkx as nx
from neo4j import GraphDatabase
import json
import logging
import time
class neo4j_exception(Exception):
"Raised when a neo4j exception araise"
pass
class Neo4jManager:
uri: str
user: str
password: str
database: str
def __init__(self, cfg=dict) -> None:
self.uri = cfg["uri"]
self.user = cfg["user"]
self.password = cfg["password"]
self.database = cfg["database"]
logging.info("try to connect to Neo4j for init testing purpose")
# just to wait for server startup and log its status
base = 2
max_tries = 3
wait_seconds = [base**i for i in range(max_tries)]
connection_success = False
for num_tries in range(max_tries):
try:
_ = self.count_nodes_neo4j()
except Exception as e:
logging.error("Problem with the Neo4j connection:%s", e)
logging.info("neo4j connection num_tries:%s", num_tries)
logging.info(
"neo4j waiting %s seconds before automatically trying again.",
wait_seconds[num_tries],
)
time.sleep(wait_seconds[num_tries])
else:
logging.info("The connection with Neo4j db is validated")
connection_success = True
break
logging.info(
"Tried %s times to create connection with Neo4j with final success:%s",
num_tries,
connection_success,
)
def save_graph_to_neo4j(self, graph_to_load: str | nx.Graph) -> None:
try:
"""_summary_
the Function load a networkx graph (or the json file) and create a corresponding neo4j graph
"""
if graph_to_load == "":
# Create the Karate Club graph using NetworkX
G = nx.karate_club_graph()
logging.info("ne4j use default graph")
create_undirected = True
else:
if isinstance(graph_to_load, str):
# To read the graph from file
with open(graph_to_load, "r") as f:
G = nx.node_link_graph(json.load(f))
logging.info(
"ne4j graph correctly loaded from file:%s", graph_to_load
)
else:
G = graph_to_load
create_undirected = not (
G.is_directed()
) # retrieve direct property from graph itself
# Connect to Neo4j
driver = GraphDatabase.driver(self.uri, auth=(self.user, self.password))
def create_graph(tx, create_undirected: bool) -> None:
# Create nodes
for node in G.nodes(data=True):
node_type = node[1].get(
"type", "Unknown"
) # use the "type" feature to create node type and the other features as standard features
# dictionary with all properties
node_properties = node[
1
] # the passed properties (also the "type" attribute is inside)
node_properties["id"] = node[
0
] # the id is not passed as property (is required to add the links)
query = f"CREATE (n:{node_type} $props)"
tx.run(query, props=node_properties)
# Create edges
for edge in G.edges(data=True):
if isinstance(edge[0], int):
id_source = "{id:" + str(edge[0]) + "}"
else:
id_source = "{id:'" + str(edge[0]) + "'}"
if isinstance(edge[1], int):
id_target = "{id:" + str(edge[1]) + "}"
else:
id_target = "{id:'" + str(edge[1]) + "'}"
edge_type = edge[2].get(
"type", "Unknown"
) # use the "type" feature to create node type and the other features as standard features
node_type_source = G.nodes[edge[0]].get(
"type", "Unknown"
) # get the node type from the link itself
node_type_target = G.nodes[edge[1]].get("type", "Unknown")
edge_properties = edge[
2
] # the passed properties (also the "type" attribute is inside)
query = (
"""
MATCH (a:"""
+ node_type_source
+ id_source
+ """), (b:"""
+ node_type_target
+ id_target
+ """)
CREATE (a)-[:"""
+ edge_type
+ """ $props]->(b)
"""
)
tx.run(query, props=edge_properties)
if (
create_undirected
): # simulate Undirected Graph (double edge direction) NB. the edge type could be different from the other direction TBD
query = (
"""
MATCH (a:"""
+ node_type_target
+ id_target
+ """), (b:"""
+ node_type_source
+ id_source
+ """)
CREATE (a)-[:"""
+ edge_type
+ """ $props]->(b)
"""
)
tx.run(query, props=edge_properties)
# Run the transaction to create the graph
with driver.session(database=self.database) as session:
session.execute_write(create_graph, create_undirected)
except Exception as e:
logging.error("Problems saving graph to neo4j db:%s", e)
raise neo4j_exception("Problems saving graph to neo4j db:%s", e)
else:
# Close the driver connection
driver.close()
def delete_graph_from_neo4j(self) -> None:
try:
# Connect to Neo4j
driver = GraphDatabase.driver(self.uri, auth=(self.user, self.password))
def delete_graph(tx) -> None:
query = f"MATCH (n) DETACH DELETE n"
tx.run(query)
# Run the transaction to create the graph
with driver.session(database=self.database) as session:
session.execute_write(delete_graph)
except Exception as e:
logging.error("Problems deleting graph from neo4j db:%s", e)
raise neo4j_exception("Problems deleting graph from neo4j db:%s", e)
else:
# Close the driver connection
driver.close()
def count_nodes_neo4j(self) -> int:
try:
result = 0
# Connect to Neo4j
driver = GraphDatabase.driver(self.uri, auth=(self.user, self.password))
def count_nodes(tx):
result = tx.run("MATCH (n) RETURN count(n) AS numberOfNodes")
return result.single()["numberOfNodes"]
# Count the nodes
with driver.session(database=self.database) as session:
result = session.read_transaction(count_nodes)
except Exception as e:
logging.error("Problems counting nodes from neo4j db:%s", e)
raise neo4j_exception("Problems counting nodes from neo4j db:%s", e)
else:
# Close the driver connection
driver.close()
return result
def count_edges_neo4j(self) -> int:
try:
result = 0
# Connect to Neo4j
driver = GraphDatabase.driver(self.uri, auth=(self.user, self.password))
def count_edges(tx):
result = tx.run("MATCH ()-[r]->()RETURN count(r) AS numberOfEdges")
return result.single()["numberOfEdges"]
# Count the nodes
with driver.session(database=self.database) as session:
result = session.read_transaction(count_edges)
except Exception as e:
logging.error("Problems counting edges from neo4j db:%s", e)
raise neo4j_exception("Problems counting edges from neo4j db:%s", e)
else:
# Close the driver connection
driver.close()
return result
def query_to_neo4j(self, passed_query: str) -> list:
try:
results = []
# Connect to Neo4j
driver = GraphDatabase.driver(self.uri, auth=(self.user, self.password))
# only read cmd allowed
if "CREATE" in passed_query.upper():
logging.warning("The NEO4j query contains CREATE: skipped")
raise Exception("The NEO4j query contains CREATE: skipped")
elif "DELETE" in passed_query.upper():
logging.warning("The NEO4j query contains DELETE: skipped")
raise Exception("The NEO4j query contains DELETE: skipped")
def query_graph(tx, passed_query) -> list:
# results = list(tx.run(passed_query))
results = tx.run(passed_query)
# Convert the result to a list of dictionaries
results_list = [
record.data() for record in results
] # record.data() method is only capturing the top-level structure without delving into the properties of the relationship. use for key, value in record.items(): instead
return results_list
with driver.session(database=self.database) as session:
results = session.read_transaction(
query_graph, passed_query
) # only read cmd allowed
except Exception as e:
logging.error("Problems quering graph from neo4j db:%s", e)
raise neo4j_exception("Problems quering graph from neo4j db:%s", e)
else:
# Close the driver connection
driver.close()
return results

View File

@ -0,0 +1,165 @@
import networkx as nx
import unicodedata
import os
import logging
import time
import math
exception_msg = "Exception: %s"
class Params:
def __init__(
self, savelog, loglevel, logmode, logpath, directory_separator
):
self.savelog = savelog
self.loglevel = loglevel
self.logmode = logmode
self.logpath = logpath
self.directory_separator = directory_separator
def return_from_env_valid(env_val, default_val):
env_val = env_val.upper() # to align with uppercase convention in env files
val = os.getenv(env_val, default_val)
return val
def remove_strange_characters(text):
# Normalize the text to NFKD form
normalized_text = unicodedata.normalize("NFKD", text)
# Encode to ASCII bytes, ignore errors, and decode back to a string
ascii_text = normalized_text.encode("ascii", "ignore").decode("ascii")
return ascii_text
def standardize_textual_entry(text):
text = text.strip()
text = text.replace(" ", "")
text = text.replace("'", "")
text = text.replace(":", "_")
text = text.replace(".", "_")
text = text.lower()
return text
def clean_str(s):
return s.strip()
def clean_value(value, is_identifier=False):
if value is None:
return ""
elif isinstance(value, float) and (math.isnan(value) or math.isinf(value)): # return None instead of "" to track these issues
return None
elif isinstance(value, str):
if is_identifier == True:
return remove_strange_characters(standardize_textual_entry(value))
else:
return clean_str(value)
else:
return value
def process_row(row, attribute_map, unique_identifier_key, unique_identifier_key2=None):
"""_summary_
Function to process each row of the DataFrame based on attribute_map and return a dictionary of valid attributes for each row. NB if attribute value non present in the column list it is setted as is, otherwise is search by column name (or combination of columns)
USE dictionary comprehension
Args:
row (_type_): dataframe row
attribute_map (_type_): the desired attribute map
unique_identifier_key (_type_): id of the node for node entity or source id for edge entity
unique_identifier_key2 (_type_): target id for edge entity
Returns:
_dictionary_: dictionary representing the node and edge id and attributes
"""
result_row = {
clean_str(key): (
clean_value(
"-".join(str(row[col]) for col in value.split("$$$")),
is_identifier=True,
)
if (key == unique_identifier_key or key == unique_identifier_key2)
else (
clean_value("-".join(str(row[col]) for col in value.split("$$$")))
if "$$$" in value
else clean_value(row[value]) if value in row else clean_value(value)
)
)
for key, value in attribute_map.items()
}
return result_row
def create_folder(root_path, directory_separator="/", next_path=""):
output_dir = root_path + directory_separator + next_path
try:
if not os.path.exists(output_dir):
os.mkdir(output_dir)
except Exception as e:
logging.error(exception_msg, e)
exit(1)
return output_dir
def disclaim_bool_string(value):
if isinstance(value, str):
if value == "True":
return True
else:
return False
elif isinstance(value, bool):
return value
def logging_startup(params):
if disclaim_bool_string(params.savelog) == True:
try:
output_dir = create_folder(
root_path=os.getcwd(),
directory_separator=params.directory_separator,
next_path="outputs",
)
except Exception as e:
print(e)
exit(1)
logging.basicConfig(
filename=output_dir + params.directory_separator + params.logpath,
filemode=params.logmode,
level=params.loglevel.upper(),
# DEBUG, INFO, WARNING, ERROR, CRITICAL. All events at or above the level will get logged
format="%(asctime)s %(levelname)-7.7s (%(threadName)-10s) %(message)s",
)
else:
logging.basicConfig(
level=params.loglevel.upper(),
format="%(asctime)s %(levelname)-7.7s (%(threadName)-10s) %(message)s",
)
logging.Formatter.converter = time.gmtime # log utc-gmt
logging.info(
"### Knowledge Graph Service is starting with params:" + str(params)
)
## get active loggers and put them to ERROR value
for name in logging.root.manager.loggerDict:
logging.getLogger(name).setLevel(logging.ERROR)
### cmdstaby has to be managed in ad-hoc way
logger = logging.getLogger("cmdstanpy")
logger.addHandler(logging.NullHandler())
logger.propagate = False
logger.setLevel(logging.ERROR)

View File

@ -0,0 +1,30 @@
# DO NOT EDIT THIS FILE WITH SENSITIVE DATA
SAVELOG=True
LOGLEVEL=INFO
LOGMODE=a
LOGPATH=/my_kg_server_log.log
DEFAULT_PERSISTENCE_PATH=persistence
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=
NEO4J_DATABASE=neo4j
LOAD_GRAPHS_FROM_PERSISTENCE=True
#KEYCLOAK_TOKEN_URL=
#KEYCLOAK_SERVER_URL=
#KEYCLOAK_CLIENT_ID=
#KEYCLOAK_CLIENT_SECRET_KEY=
#KEYCLOAK_REALM_NAME=
#KEYCLOACK_ADMIN_ROLE=
#KEYCLOACK_EDITOR_ROLE=
# Define the MinIO endpoint and credentials
#MINIO_ENDPOINT=
#MINIO_ACCESS_KEY=
#MINIO_SECRET_KEY=

View File

@ -0,0 +1,322 @@
2026-06-18 15:49:31,710 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x000001DDBFC6F490>
2026-06-18 15:49:31,711 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 15:49:31,712 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 15:49:31,714 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 15:49:31,714 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 15:49:31,715 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 15:49:32,715 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 15:49:32,715 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 15:49:32,715 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 15:49:32,715 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 15:49:34,718 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 15:49:34,718 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 15:49:34,718 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 15:49:34,718 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 15:49:38,719 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 15:49:38,720 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 15:51:35,020 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000015330E0C3D0>
2026-06-18 15:51:35,020 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 15:51:35,020 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 15:51:35,022 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 15:51:35,022 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 15:51:35,023 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 15:51:36,024 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 15:51:36,025 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 15:51:36,025 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 15:51:36,025 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 15:51:38,027 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 15:51:38,027 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 15:51:38,027 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 15:51:38,028 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 15:51:42,029 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 15:51:42,029 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 15:51:42,029 INFO (MainThread) populate graph from a pre-existing one
2026-06-18 15:51:42,041 ERROR (MainThread) key error exception:'accessibility_graph'
2026-06-18 15:51:42,042 ERROR (MainThread) exception:Graph key-name error exception:'accessibility_graph'
2026-06-18 15:51:42,042 INFO (MainThread) saving graph to neo4j...
2026-06-18 15:51:57,269 INFO (MainThread) graph saved to neo4j
2026-06-18 15:51:57,269 INFO (MainThread) graph accessibility_graph correctly created
2026-06-18 16:02:53,105 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000023937D88810>
2026-06-18 16:02:53,106 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 16:02:53,106 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:02:53,108 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:02:53,108 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 16:02:53,109 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 16:02:54,111 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:02:54,111 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:02:54,111 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 16:02:54,112 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 16:02:56,113 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:02:56,114 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:02:56,114 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 16:02:56,114 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 16:03:00,116 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 16:03:00,116 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 16:03:00,116 INFO (MainThread) populate graph from a pre-existing one
2026-06-18 16:03:00,123 ERROR (MainThread) key error exception:'accessibility_graph'
2026-06-18 16:03:00,124 ERROR (MainThread) exception:Graph key-name error exception:'accessibility_graph'
2026-06-18 16:03:00,124 INFO (MainThread) saving graph to neo4j...
2026-06-18 16:03:04,215 INFO (MainThread) graph saved to neo4j
2026-06-18 16:03:04,215 INFO (MainThread) graph accessibility_graph correctly created
2026-06-18 16:05:43,986 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x000001AC3EA27490>
2026-06-18 16:05:43,986 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 16:05:43,987 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:05:43,988 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:05:43,988 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 16:05:43,989 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 16:05:44,990 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:05:44,991 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:05:44,991 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 16:05:44,991 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 16:05:46,993 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:05:46,993 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:05:46,994 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 16:05:46,994 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 16:05:50,995 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 16:05:50,995 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph.json', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 16:05:50,996 INFO (MainThread) populate graph from a pre-existing one
2026-06-18 16:05:51,012 ERROR (MainThread) key error exception:'accessibility_graph.json'
2026-06-18 16:05:51,012 ERROR (MainThread) exception:Graph key-name error exception:'accessibility_graph.json'
2026-06-18 16:05:51,012 INFO (MainThread) saving graph to neo4j...
2026-06-18 16:05:54,903 INFO (MainThread) graph saved to neo4j
2026-06-18 16:05:54,904 INFO (MainThread) graph accessibility_graph.json correctly created
2026-06-18 16:10:41,831 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x00000233DFD37ED0>
2026-06-18 16:10:41,832 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 16:10:41,832 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:10:41,834 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:10:41,834 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 16:10:41,834 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 16:10:42,837 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:10:42,837 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:10:42,838 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 16:10:42,838 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 16:10:44,839 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:10:44,839 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:10:44,840 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 16:10:44,840 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 16:10:48,841 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 16:10:48,841 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 16:10:48,842 INFO (MainThread) populate graph from a pre-existing one
2026-06-18 16:10:48,908 INFO (MainThread) saving graph to neo4j...
2026-06-18 16:10:52,256 INFO (MainThread) graph saved to neo4j
2026-06-18 16:10:52,256 INFO (MainThread) graph accessibility_graph correctly created
2026-06-18 16:17:40,681 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000018E43FF90D0>
2026-06-18 16:17:40,682 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 16:17:40,682 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:17:40,685 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:17:40,685 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 16:17:40,685 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 16:17:41,686 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:17:41,686 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:17:41,686 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 16:17:41,686 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 16:17:43,689 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:17:43,689 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:17:43,689 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 16:17:43,689 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 16:17:47,691 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 16:17:47,691 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 16:17:47,691 INFO (MainThread) populate graph from a pre-existing one
2026-06-18 16:17:47,756 INFO (MainThread) saving graph to neo4j...
2026-06-18 16:17:50,786 INFO (MainThread) graph saved to neo4j
2026-06-18 16:17:50,786 INFO (MainThread) graph accessibility_graph correctly created
2026-06-18 16:27:41,322 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000022CE0C48850>
2026-06-18 16:27:41,322 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 16:27:41,323 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:27:41,328 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:27:41,328 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 16:27:41,328 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 16:27:42,330 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:27:42,330 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:27:42,330 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 16:27:42,331 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 16:27:44,331 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:27:44,331 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:27:44,331 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 16:27:44,331 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 16:27:48,333 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 16:27:48,333 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 16:27:48,333 INFO (MainThread) populate graph from a pre-existing one
2026-06-18 16:27:48,402 INFO (MainThread) saving graph to neo4j...
2026-06-18 16:27:59,845 INFO (MainThread) graph saved to neo4j
2026-06-18 16:27:59,846 INFO (MainThread) graph accessibility_graph correctly created
2026-06-18 16:41:07,811 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000017057057FD0>
2026-06-18 16:41:07,812 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-18 16:41:07,812 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:41:07,814 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:41:07,814 INFO (MainThread) neo4j connection num_tries:0
2026-06-18 16:41:07,815 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-18 16:41:08,816 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:41:08,816 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:41:08,817 INFO (MainThread) neo4j connection num_tries:1
2026-06-18 16:41:08,817 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-18 16:41:10,819 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-18 16:41:10,819 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-18 16:41:10,819 INFO (MainThread) neo4j connection num_tries:2
2026-06-18 16:41:10,819 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-18 16:41:14,820 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-18 16:41:14,820 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-18 16:41:14,821 INFO (MainThread) populate graph from a pre-existing one
2026-06-18 16:41:14,905 INFO (MainThread) saving graph to neo4j...
2026-06-18 16:41:25,898 INFO (MainThread) graph saved to neo4j
2026-06-18 16:41:25,898 INFO (MainThread) graph accessibility_graph correctly created
2026-06-19 13:44:09,295 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x000001813F92F490>
2026-06-19 13:44:09,296 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-19 13:44:09,296 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-19 13:44:09,298 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-19 13:44:09,298 INFO (MainThread) neo4j connection num_tries:0
2026-06-19 13:44:09,298 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-19 13:44:10,300 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-19 13:44:10,301 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-19 13:44:10,301 INFO (MainThread) neo4j connection num_tries:1
2026-06-19 13:44:10,301 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-19 13:44:12,302 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-19 13:44:12,303 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-19 13:44:12,303 INFO (MainThread) neo4j connection num_tries:2
2026-06-19 13:44:12,303 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-19 13:44:16,305 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-19 13:44:16,305 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='graph.json')
2026-06-19 13:44:16,305 INFO (MainThread) populate graph from a pre-existing one
2026-06-19 13:44:16,355 INFO (MainThread) saving graph to neo4j...
2026-06-19 13:44:40,038 INFO (MainThread) graph saved to neo4j
2026-06-19 13:44:40,038 INFO (MainThread) graph accessibility_graph correctly created
2026-06-20 09:14:34,331 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000024621CF7490>
2026-06-20 09:14:34,331 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-20 09:14:34,332 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:14:34,334 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:14:34,334 INFO (MainThread) neo4j connection num_tries:0
2026-06-20 09:14:34,334 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-20 09:14:35,337 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:14:35,338 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:14:35,338 INFO (MainThread) neo4j connection num_tries:1
2026-06-20 09:14:35,338 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-20 09:14:37,340 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:14:37,340 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:14:37,341 INFO (MainThread) neo4j connection num_tries:2
2026-06-20 09:14:37,341 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-20 09:14:41,343 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-20 09:14:41,343 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='../graph.json')
2026-06-20 09:14:41,344 INFO (MainThread) populate graph from a pre-existing one
2026-06-20 09:14:41,418 INFO (MainThread) saving graph to neo4j...
2026-06-20 09:15:22,264 INFO (MainThread) graph saved to neo4j
2026-06-20 09:15:22,265 INFO (MainThread) graph accessibility_graph correctly created
2026-06-20 09:16:58,519 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000020FAB4E7490>
2026-06-20 09:16:58,519 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-20 09:16:58,520 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:16:58,523 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:16:58,523 INFO (MainThread) neo4j connection num_tries:0
2026-06-20 09:16:58,523 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-20 09:16:59,533 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:16:59,533 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:16:59,533 INFO (MainThread) neo4j connection num_tries:1
2026-06-20 09:16:59,533 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-20 09:17:01,535 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:17:01,535 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:17:01,535 INFO (MainThread) neo4j connection num_tries:2
2026-06-20 09:17:01,535 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-20 09:17:05,537 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-20 09:17:05,537 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='../graph.json')
2026-06-20 09:17:05,539 INFO (MainThread) populate graph from a pre-existing one
2026-06-20 09:17:05,619 INFO (MainThread) saving graph to neo4j...
2026-06-20 09:17:30,646 INFO (MainThread) graph saved to neo4j
2026-06-20 09:17:30,646 INFO (MainThread) graph accessibility_graph correctly created
2026-06-20 09:49:00,961 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x00000184E4217490>
2026-06-20 09:49:00,961 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-20 09:49:00,962 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:49:00,964 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:49:00,964 INFO (MainThread) neo4j connection num_tries:0
2026-06-20 09:49:00,964 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-20 09:49:01,965 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:49:01,966 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:49:01,966 INFO (MainThread) neo4j connection num_tries:1
2026-06-20 09:49:01,966 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-20 09:49:03,966 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 09:49:03,966 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 09:49:03,967 INFO (MainThread) neo4j connection num_tries:2
2026-06-20 09:49:03,967 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-20 09:49:07,968 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-20 09:49:07,968 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='../graph.json')
2026-06-20 09:49:07,968 INFO (MainThread) populate graph from a pre-existing one
2026-06-20 09:49:08,029 INFO (MainThread) saving graph to neo4j...
2026-06-20 09:49:30,129 INFO (MainThread) graph saved to neo4j
2026-06-20 09:49:30,130 INFO (MainThread) graph accessibility_graph correctly created
2026-06-20 13:34:35,193 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x00000274213E7FD0>
2026-06-20 13:34:35,194 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-20 13:34:35,195 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 13:34:35,197 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 13:34:35,197 INFO (MainThread) neo4j connection num_tries:0
2026-06-20 13:34:35,197 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-20 13:34:36,198 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 13:34:36,198 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 13:34:36,198 INFO (MainThread) neo4j connection num_tries:1
2026-06-20 13:34:36,198 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-20 13:34:38,199 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 13:34:38,199 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 13:34:38,199 INFO (MainThread) neo4j connection num_tries:2
2026-06-20 13:34:38,199 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-20 13:34:42,201 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-20 13:34:42,201 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='../graph.json')
2026-06-20 13:34:42,201 INFO (MainThread) populate graph from a pre-existing one
2026-06-20 13:34:42,273 INFO (MainThread) saving graph to neo4j...
2026-06-20 13:34:59,859 INFO (MainThread) graph saved to neo4j
2026-06-20 13:34:59,859 INFO (MainThread) graph accessibility_graph correctly created
2026-06-20 15:31:25,130 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x000001DAA68A7490>
2026-06-20 15:31:25,130 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-20 15:31:25,130 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 15:31:25,133 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 15:31:25,133 INFO (MainThread) neo4j connection num_tries:0
2026-06-20 15:31:25,133 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-20 15:31:26,140 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 15:31:26,140 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 15:31:26,140 INFO (MainThread) neo4j connection num_tries:1
2026-06-20 15:31:26,140 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-20 15:31:28,258 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 15:31:28,258 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 15:31:28,258 INFO (MainThread) neo4j connection num_tries:2
2026-06-20 15:31:28,258 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-20 15:31:32,260 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-20 15:31:32,260 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='../graph.json')
2026-06-20 15:31:32,260 INFO (MainThread) populate graph from a pre-existing one
2026-06-20 15:31:32,328 INFO (MainThread) saving graph to neo4j...
2026-06-20 15:31:47,530 INFO (MainThread) graph saved to neo4j
2026-06-20 15:31:47,530 INFO (MainThread) graph accessibility_graph correctly created
2026-06-20 15:56:18,557 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x000002438EF77490>
2026-06-20 15:56:18,558 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-20 15:56:18,558 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 15:56:18,560 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 15:56:18,561 INFO (MainThread) neo4j connection num_tries:0
2026-06-20 15:56:18,561 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-20 15:56:19,562 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 15:56:19,563 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 15:56:19,563 INFO (MainThread) neo4j connection num_tries:1
2026-06-20 15:56:19,563 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-20 15:56:21,563 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 15:56:21,563 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 15:56:21,563 INFO (MainThread) neo4j connection num_tries:2
2026-06-20 15:56:21,563 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-20 15:56:25,564 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-20 15:56:25,564 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='../graph.json')
2026-06-20 15:56:25,564 INFO (MainThread) populate graph from a pre-existing one
2026-06-20 15:56:25,615 INFO (MainThread) saving graph to neo4j...
2026-06-20 15:56:38,180 INFO (MainThread) graph saved to neo4j
2026-06-20 15:56:38,181 INFO (MainThread) graph accessibility_graph correctly created
2026-06-20 16:18:36,059 INFO (MainThread) ### Knowledge Graph Service is starting with params:<dependences.utils.utils.Params object at 0x0000018832D47490>
2026-06-20 16:18:36,059 INFO (MainThread) try to connect to Neo4j for init testing purpose
2026-06-20 16:18:36,060 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 16:18:36,063 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 16:18:36,063 INFO (MainThread) neo4j connection num_tries:0
2026-06-20 16:18:36,063 INFO (MainThread) neo4j waiting 1 seconds before automatically trying again.
2026-06-20 16:18:37,064 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 16:18:37,064 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 16:18:37,064 INFO (MainThread) neo4j connection num_tries:1
2026-06-20 16:18:37,064 INFO (MainThread) neo4j waiting 2 seconds before automatically trying again.
2026-06-20 16:18:39,066 ERROR (MainThread) Problems counting nodes from neo4j db:'Session' object has no attribute 'read_transaction'
2026-06-20 16:18:39,066 ERROR (MainThread) Problem with the Neo4j connection:('Problems counting nodes from neo4j db:%s', AttributeError("'Session' object has no attribute 'read_transaction'"))
2026-06-20 16:18:39,066 INFO (MainThread) neo4j connection num_tries:2
2026-06-20 16:18:39,066 INFO (MainThread) neo4j waiting 4 seconds before automatically trying again.
2026-06-20 16:18:43,100 INFO (MainThread) Tried 2 times to create connection with Neo4j with final success:False
2026-06-20 16:18:43,100 INFO (MainThread) kg manager started with cli args:Namespace(graph_type='directed', graph_name='accessibility_graph', save_to_neo4j=True, from_graph='../graph.json')
2026-06-20 16:18:43,101 INFO (MainThread) populate graph from a pre-existing one
2026-06-20 16:18:43,531 INFO (MainThread) saving graph to neo4j...
2026-06-20 16:18:54,946 INFO (MainThread) graph saved to neo4j
2026-06-20 16:18:54,946 INFO (MainThread) graph accessibility_graph correctly created

File diff suppressed because one or more lines are too long

View File

@ -854,15 +854,16 @@ class TargetSizeExtractor:
#vp_all_rel = _to_viewport_coords(vp_all_rest, scroll_y)
vp_all_rel = _to_viewport_coords(vp_all, scroll_y)
annotated_img = _draw_bounding_box_on_bytes(raw_png, vp_all_rel)
#annotated_img = _draw_bounding_box_on_bytes(raw_png, vp_all_rel)
annotated_img = _draw_bounding_box_on_bytes(raw_png, [])# to avoid all the bounding box drawing
# Draw orange first (larger radius) → red on top
annotated_img = _draw_circles_on_bytes(
"""annotated_img = _draw_circles_on_bytes(
_pil_to_png_bytes(annotated_img),
vp_44_only_rel,
self.COLOR_44,
radius=self.THRESHOLD_ENHANCED // 2, # 22 px
)
)"""
annotated_img = _draw_circles_on_bytes(
_pil_to_png_bytes(annotated_img),
vp_24_rel,

File diff suppressed because it is too large Load Diff