RiC-O — In-Memory Demo (NetworkX)
This notebook demonstrates how to model a RiC-O (Records in Contexts — Ontology) graph using the DRM tools with the NetworkX in-memory backend.
Key concepts:
NetworkXGraphstores everything in memory with optional pickle persistenceSame DRM entity classes (
Thing,Agent,RecordResource, etc.) as Neo4jSame
load_ric_o_naf()loader downloads real RiC-O data from GitHubget_subdocuments()traverses_propagateedges (BFS)Cypher queries are supported via an in-memory executor
Persistence file is created fresh each run in
/tmp/
Steps:
Create an in-memory NetworkX graph with unique persistence path
Load real RiC-O data from the National Archives of France (NAF) example
Initialize propagation properties
Query subdocuments
Inspect the hierarchy
Summary statistics
Why NetworkX?
NetworkX is useful for:
Prototyping — no external database needed
Testing — fast, deterministic, easy to clean up
Learning — see the graph structure without Neo4j overhead
Persistence — pickle file survives notebook restarts (optional)
[1]:
import importlib.util
# Check if drm is installed
package_to_check = 'drm'
spec = importlib.util.find_spec(package_to_check)
if spec is None:
print(f'⚠️ {package_to_check} not installed. Installing...')
%pip install -q --upgrade drm-tools
print("✅ Installation complete.")
else:
print(f'✅ {package_to_check} already installed. Skipping.')
✅ drm already installed. Skipping.
[2]:
import os
import sys
import uuid
from pathlib import Path
# Read .env file manually (no python-dotenv dependency)
def read_env(env_path):
"""Read a .env file and return a dict of key=value pairs."""
env_vars = {}
with open(env_path, 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '=' in line:
key, value = line.split('=', 1)
env_vars[key.strip()] = value.strip()
return env_vars
# Find .env in repo root
_env_path = Path.cwd() / ".env"
if not _env_path.exists():
for parent in Path.cwd().parents:
candidate = parent / ".env"
if candidate.exists():
_env_path = candidate
break
if _env_path.exists():
_env_vars = read_env(_env_path)
for key, value in _env_vars.items():
os.environ.setdefault(key, value)
print(f"✅ Loaded .env from {_env_path}")
print(f" Found {len(_env_vars)} variables")
else:
print("⚠️ .env file not found. NetworkX backend doesn't need credentials.")
# Add drm package to path for development
_repo_root = Path.cwd()
if not (_repo_root / "drm").exists():
for parent in Path.cwd().parents:
if (parent / "drm").exists():
_repo_root = parent
break
# Add repo root to sys.path so local drm loads before PyPI version
if str(_repo_root) not in sys.path:
sys.path.insert(0, str(_repo_root))
print(f"✅ Added {_repo_root} to sys.path")
from drm.networkx_graph import NetworkXGraph
from drm.rico_entities import (
Thing,
Agent, Person, CorporateBody, AgentName, Appellation,
Date, Place, PlaceName, Event, Activity,
RecordResource, Instantiation, Title,
Group, Name,
)
def section(title):
"""Print a section header."""
print(f"\n{'=' * 60}")
print(f" {title}")
print(f"{'=' * 60}")
def sub_section(title):
"""Print a subsection header."""
print(f"\n--- {title} ---")
print("✅ All imports successful.")
✅ Loaded .env from /Users/oriol/Desenvolupament/drm-tools/.env
Found 5 variables
✅ Added /Users/oriol/Desenvolupament/drm-tools to sys.path
✅ All imports successful.
Step 1: Create in-memory graph with unique persistence path
The NetworkX backend stores data in memory. A pickle file is used for persistence across notebook restarts. Each run creates a unique file using a UUID, so previous runs never interfere.
[3]:
import os
import tempfile
# Generate a unique persistence file for this run
_run_id = uuid.uuid4().hex[:8]
_persistence_file = os.path.join(
tempfile.gettempdir(),
f"ric_o_networkx_{_run_id}.pkl"
)
print(f"📁 Persistence file: {_persistence_file}")
print(f" (unique per run — safe to delete after)")
# Create the in-memory graph
graph = NetworkXGraph(persistence_path=_persistence_file)
print(f"\n✅ NetworkXGraph created.")
print(f" Nodes in memory: {len(graph._node_attrs)}")
print(f" Edges in memory: {len(graph._graph.edges())}")
print(f" Persistence enabled: {graph._persistence_path is not None}")
📁 Persistence file: /var/folders/rm/5_s35r9j3198mxxpvlpfx8700000gn/T/ric_o_networkx_e073bd7a.pkl
(unique per run — safe to delete after)
✅ NetworkXGraph created.
Nodes in memory: 0
Edges in memory: 0
Persistence enabled: True
Step 2: Load real RiC-O data from the National Archives of France
The DRM tools include a loader that downloads and parses RiC-O RDF/XML data from the ICA-EGAD/RiC-O repository.
What gets loaded:
RDF Type |
DRM Entity |
Parent |
|---|---|---|
|
|
— (strong node) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hierarchy example:
Thing (strong)
├── Agent (WeakNode)
│ ├── AgentName (WeakNode of Agent)
│ └── Instantiation (WeakNode of Agent)
└── RecordResource (WeakNode)
└── Instantiation (WeakNode of RecordResource)
The loader downloads files from GitHub, parses the RDF/XML, and inserts entities into the graph with proper parent-child relationships.
[4]:
from drm.exemples import load_ric_o_naf
section("Loading RiC-O data from National Archives of France")
# Load real RiC-O data
# limit: total entities to load (soft limit)
# max_agents: number of agent files to load
# max_records: number of record resource files to load
stats = load_ric_o_naf(
graph,
limit=50,
max_agents=10,
max_records=3,
)
print(f"\n📊 Load statistics:")
print(f" Agents loaded: {stats['agents']}")
print(f" Records loaded: {stats['records']}")
print(f" Things created: {stats['things']}")
print(f" Child entities: {stats.get('children', '?')}")
print(f" Total entities: {stats['total']}")
============================================================
Loading RiC-O data from National Archives of France
============================================================
📊 Load statistics:
Agents loaded: 10
Records loaded: 27
Things created: 40
Child entities: 75
Total entities: 152
Step 3: Initialize propagation properties
Scan the graph and initialize _propagate flags on edges between parent and child nodes. This enables cascade delete behavior.
In NetworkX, this is much faster than Neo4j since everything is in memory.
[5]:
# Check state before init (using internal attributes)
sub_section("Before init_propagation()")
total_before = len(graph._node_attrs)
weak_before = sum(1 for a in graph._node_attrs.values() if a.get('is_weak'))
done_before = sum(1 for a in graph._node_attrs.values() if a.get('_weak_init_done'))
print(f" Total nodes: {total_before}")
print(f" Nodes with is_weak: {weak_before}")
print(f" Nodes with _weak_init_done: {done_before}")
# Run initialization
print("\nRunning init_propagation()...")
init_result = graph.init_propagation()
print(f"Result: {init_result}")
# Check state after init
sub_section("After init_propagation()")
total_after = len(graph._node_attrs)
weak_after = sum(1 for a in graph._node_attrs.values() if a.get('is_weak'))
done_after = sum(1 for a in graph._node_attrs.values() if a.get('_weak_init_done'))
print(f" Total nodes: {total_after}")
print(f" Nodes with is_weak: {weak_after}")
print(f" Nodes with _weak_init_done: {done_after}")
# Count propagation edges
prop_count = sum(
1 for u, v, k, d in graph._graph.edges(data=True, keys=True)
if graph.get_edge_attrs(u, v, d.get('rel_type', k)).get('_propagate') is True
)
print(f" Edges with _propagate: {prop_count}")
--- Before init_propagation() ---
Total nodes: 152
Nodes with is_weak: 0
Nodes with _weak_init_done: 0
Running init_propagation()...
Result: True
--- After init_propagation() ---
Total nodes: 152
Nodes with is_weak: 112
Nodes with _weak_init_done: 0
Edges with _propagate: 112
Step 4: Inspect the loaded graph structure
Query the graph to see what entities were loaded and how they are organized. We use the internal _node_attrs and _edge_attrs dicts for inspection, which works regardless of Cypher executor limitations.
[6]:
# Show all Things (strong nodes)
sub_section("Nodes by label")
from collections import Counter
label_counts = Counter(a.get('main_label', 'Node') for a in graph._node_attrs.values())
for label, count in label_counts.most_common():
print(f" {label}: {count}")
# Show relations by type
sub_section("Relations by type")
rel_counts = Counter()
for u, v, k, d in graph._graph.edges(data=True, keys=True):
edge_attrs = graph.get_edge_attrs(u, v, d.get('rel_type', k))
rel_type = edge_attrs.get('_propagate', False) and 'HAS_*' or d.get('rel_type', k)
rel_counts[rel_type] += 1
for rel_type, count in rel_counts.most_common():
print(f" {rel_type}: {count}")
--- Nodes by label ---
AgentName: 48
Thing: 40
RecordResource: 27
Instantiation: 27
Agent: 10
--- Relations by type ---
HAS_*: 112
[7]:
# Show all Things (strong nodes)
sub_section("All Things (strong nodes)")
things = [
(nid, a.get('identifier', '?'), a.get('title', a.get('identifier', '?')))
for nid, a in graph._node_attrs.items()
if a.get('main_label') == 'Thing'
]
things.sort(key=lambda x: x[1])
for nid, identifier, name in things:
print(f" {identifier}: {name}")
--- All Things (strong nodes) ---
record-000005: record-000005
record-000016: record-000016
record-000051: record-000051
record-003500: record-003500
record-003529: record-003529
record-003530: record-003530
record-003531: record-003531
record-003532: record-003532
record-003549: record-003549
record-003550: record-003550
record-003551: record-003551
record-007375: record-007375
record-009555: record-009555
thing-rr-recordResource-recordResource-003500-d_1: thing-rr-recordResource-recordResource-003500-d_1
thing-rr-recordResource-recordResource-003500-d_2: thing-rr-recordResource-recordResource-003500-d_2
thing-rr-recordResource-recordResource-003500-d_3: thing-rr-recordResource-recordResource-003500-d_3
thing-rr-recordResource-recordResource-003500-d_4: thing-rr-recordResource-recordResource-003500-d_4
thing-rr-recordResource-recordResource-007375-d_1: thing-rr-recordResource-recordResource-007375-d_1
thing-rr-recordResource-recordResource-007375-d_2: thing-rr-recordResource-recordResource-007375-d_2
thing-rr-recordResource-recordResource-009555-d_1: thing-rr-recordResource-recordResource-009555-d_1
thing-rr-recordResource-recordResource-009555-d_10: thing-rr-recordResource-recordResource-009555-d_10
thing-rr-recordResource-recordResource-009555-d_11: thing-rr-recordResource-recordResource-009555-d_11
thing-rr-recordResource-recordResource-009555-d_12: thing-rr-recordResource-recordResource-009555-d_12
thing-rr-recordResource-recordResource-009555-d_13: thing-rr-recordResource-recordResource-009555-d_13
thing-rr-recordResource-recordResource-009555-d_14: thing-rr-recordResource-recordResource-009555-d_14
thing-rr-recordResource-recordResource-009555-d_15: thing-rr-recordResource-recordResource-009555-d_15
thing-rr-recordResource-recordResource-009555-d_16: thing-rr-recordResource-recordResource-009555-d_16
thing-rr-recordResource-recordResource-009555-d_17: thing-rr-recordResource-recordResource-009555-d_17
thing-rr-recordResource-recordResource-009555-d_18: thing-rr-recordResource-recordResource-009555-d_18
thing-rr-recordResource-recordResource-009555-d_2: thing-rr-recordResource-recordResource-009555-d_2
thing-rr-recordResource-recordResource-009555-d_3: thing-rr-recordResource-recordResource-009555-d_3
thing-rr-recordResource-recordResource-009555-d_4: thing-rr-recordResource-recordResource-009555-d_4
thing-rr-recordResource-recordResource-009555-d_5: thing-rr-recordResource-recordResource-009555-d_5
thing-rr-recordResource-recordResource-009555-d_6: thing-rr-recordResource-recordResource-009555-d_6
thing-rr-recordResource-recordResource-009555-d_7: thing-rr-recordResource-recordResource-009555-d_7
thing-rr-recordResource-recordResource-009555-d_8: thing-rr-recordResource-recordResource-009555-d_8
thing-rr-recordResource-recordResource-009555-d_9: thing-rr-recordResource-recordResource-009555-d_9
thing-rr-recordResource-recordResource-top-003500: thing-rr-recordResource-recordResource-top-003500
thing-rr-recordResource-recordResource-top-007375: thing-rr-recordResource-recordResource-top-007375
thing-rr-recordResource-recordResource-top-009555: thing-rr-recordResource-recordResource-top-009555
Step 5: Get subdocuments
Use get_subdocuments() to retrieve all weak descendants of each Thing. This traverses only edges with _propagate=True using BFS.
[8]:
# Show subdocuments for each Thing
for nid, identifier, name in things:
thing_node = Thing(pk={"identifier": identifier})
subdocs = graph.get_subdocuments(thing_node)
sub_section(f"Subdocuments of Thing({identifier})")
print(f" Found {len(subdocs)} subdocuments:")
# Group by label
by_label = {}
for sd in subdocs:
label = sd['label']
if label not in by_label:
by_label[label] = []
by_label[label].append(sd)
for label in sorted(by_label.keys()):
items = by_label[label]
for item in items[:5]: # Show max 5 per label
name = item['pk'].get('fullName', item['pk'].get('name', item['pk'].get('agentId', '?')))
print(f" - {label}: {name}")
if len(items) > 5:
print(f" ... and {len(items) - 5} more")
--- Subdocuments of Thing(record-000005) ---
Found 13 subdocuments:
- Agent: agent-agent-000005
- AgentName: France. Ministère de la Culture et de la Communication (1959-....)
- AgentName: Ministère de la Culture
- AgentName: Ministère de la Culture et de la Communication
- AgentName: Ministère de la Culture, de la Communication, des Grands Travaux et du Bicentenaire
- AgentName: Ministère de la Culture et de la Communication
... and 7 more
--- Subdocuments of Thing(record-000016) ---
Found 11 subdocuments:
- Agent: agent-agent-000016
- AgentName: France. Ministère de l'Éducation nationale (1828-....)
- AgentName: Ministère de l'Éducation nationale, de la Jeunesse et de la Vie associative
- AgentName: Ministère de l'Éducation nationale
- AgentName: Ministère de l'Éducation nationale, de l'Enseignement supérieur et de la Recherche
- AgentName: Ministère de la Jeunesse, de l'Éducation nationale et de la Recherche
... and 5 more
--- Subdocuments of Thing(record-000051) ---
Found 4 subdocuments:
- Agent: agent-agent-000051
- AgentName: France. Ministère des affaires culturelles. Direction de l'architecture (1959-1978)
- AgentName: France. Ministère de la culture et de l'environnement. Direction de l'architecture
- AgentName: France. Secrétariat d'État à la culture. Direction de l'architecture
--- Subdocuments of Thing(record-003500) ---
Found 0 subdocuments:
--- Subdocuments of Thing(record-003529) ---
Found 8 subdocuments:
- Agent: agent-agent-003529
- AgentName: France. Direction des bibliothèques et de la lecture publique. Bureau de la gestion et du contrôle financiers (1945-1975)
- AgentName: Bureau de la gestion et du contrôle financier
- AgentName: Deuxième bureau (Gestion et contrôle financier, Affaires générales-Documentation-Matériel)
- AgentName: Bureau de la gestion et du contrôle financiers
- AgentName: Bureau de la comptabilité
... and 2 more
--- Subdocuments of Thing(record-003530) ---
Found 4 subdocuments:
- Agent: agent-agent-003530
- AgentName: France. Direction des bibliothèques et de la lecture publique. Division des services administratifs. Bureau des affaires générales (1965-1975)
- AgentName: DB 3
- AgentName: BL 3
--- Subdocuments of Thing(record-003531) ---
Found 7 subdocuments:
- Agent: agent-agent-003531
- AgentName: France. Direction des bibliothèques et de la lecture publique. Bureau du personnel (1945-1975)
- AgentName: Bureau du personnel
- AgentName: Bureau du personnel et des affaires générales
- AgentName: Bureau du personnel
- AgentName: BL 1
... and 1 more
--- Subdocuments of Thing(record-003532) ---
Found 4 subdocuments:
- Agent: agent-agent-003532
- AgentName: France. Direction des bibliothèques et de la lecture publique. Division des affaires administratives (1965-1975)
- AgentName: Services administratifs
- AgentName: Division des affaires administratives
--- Subdocuments of Thing(record-003549) ---
Found 2 subdocuments:
- Agent: agent-agent-003549
- AgentName: France. Direction des bibliothèques et de la lecture publique. Services techniques. Section des bibliothèques d'étude et de recherche (1971-1975)
--- Subdocuments of Thing(record-003550) ---
Found 2 subdocuments:
- Agent: agent-agent-003550
- AgentName: France. Direction des bibliothèques et de la lecture publique. Services techniques. Section des affaires communes (1971-1975)
--- Subdocuments of Thing(record-003551) ---
Found 3 subdocuments:
- Agent: agent-agent-003551
- AgentName: France. Direction des bibliothèques et de la lecture publique. Services techniques. Section de la lecture publique (1968-1975)
- AgentName: Service de la lecture publique
--- Subdocuments of Thing(record-007375) ---
Found 0 subdocuments:
--- Subdocuments of Thing(record-009555) ---
Found 0 subdocuments:
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-003500-d_1) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-003500-d_2) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-003500-d_3) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-003500-d_4) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-007375-d_1) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-007375-d_2) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_1) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_10) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_11) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_12) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_13) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_14) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_15) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_16) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_17) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_18) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_2) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_3) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_4) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_5) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_6) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_7) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_8) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-009555-d_9) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-top-003500) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-top-007375) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
--- Subdocuments of Thing(thing-rr-recordResource-recordResource-top-009555) ---
Found 2 subdocuments:
- Instantiation: ?
- RecordResource: ?
Step 6: Query the hierarchy paths
Visualize the full hierarchy for a sample Thing by walking the NetworkX graph directly. This avoids Cypher executor limitations for path traversal.
[9]:
def _walk_hierarchy(graph, start_id, max_depth=6):
"""Walk the graph from start_id and return all reachable nodes."""
paths = []
queue = [(start_id, [start_id], [])]
while queue:
current, path_nodes, path_edges = queue.pop(0)
if len(path_nodes) > 1:
paths.append((path_nodes, path_edges))
if len(path_nodes) >= max_depth:
continue
# MultiDiGraph.edges(u, data=True, keys=True) yields (u, v, key, data)
for u, v, edge_key, edge_data in graph._graph.edges(current, data=True, keys=True):
edge_attrs = graph.get_edge_attrs(u, v, edge_data.get('rel_type', edge_key))
rel_type = edge_attrs.get('_propagate', False) and 'parent' or edge_data.get('rel_type', 'unknown')
paths.append((path_nodes + [v], path_edges + [(u, v, rel_type)]))
if len(path_nodes) < max_depth - 1:
queue.append((v, path_nodes + [v], path_edges + [(u, v, rel_type)]))
return paths
# Walk hierarchy for first Thing
if things:
first_nid, first_id, first_name = things[0]
sub_section(f"Hierarchy: Thing({first_id})")
paths = _walk_hierarchy(graph, first_nid)
for path_nodes, path_edges in paths:
parts = []
for node_id in path_nodes:
attrs = graph._node_attrs.get(node_id, {})
label = attrs.get('main_label', 'Node')
name = attrs.get('fullName', attrs.get('name', attrs.get('agentId', attrs.get('recordResourceId', str(node_id)))))
parts.append(f"{label}({name})")
for src, dst, rel_type in path_edges:
parts.append(f"-[{rel_type}]->")
print(f" {' → '.join(parts)}")
--- Hierarchy: Thing(record-000005) ---
Thing(string) → Agent(agent-agent-000005) → -[parent]->
Thing(string) → Agent(agent-agent-000005) → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(France. Ministère de la Culture et de la Communication (1959-....)) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Communication) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture, de la Communication, des Grands Travaux et du Bicentenaire) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Communication) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Communication) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de l'Environnement) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Secrétariat d'État à la Culture) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère des Affaires culturelles) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère des Affaires culturelles et de l'Environnement) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Francophonie) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(France. Ministère de la Culture et de la Communication (1959-....)) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Communication) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture, de la Communication, des Grands Travaux et du Bicentenaire) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Communication) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Communication) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de l'Environnement) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Secrétariat d'État à la Culture) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère des Affaires culturelles) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère des Affaires culturelles et de l'Environnement) → -[parent]-> → -[parent]->
Thing(string) → Agent(agent-agent-000005) → AgentName(Ministère de la Culture et de la Francophonie) → -[parent]-> → -[parent]->
Step 7: Inspect specific entities
Look at the properties of loaded entities to understand the data model.
[10]:
# Show sample Agent entities
sub_section("Sample Agent entities")
agents = [
(nid, a.get('agentId', '?'), a.get('label', 'No label'),
a.get('beginningDate', ''), a.get('endDate', ''))
for nid, a in graph._node_attrs.items()
if a.get('main_label') in ('Agent', 'CorporateBody', 'Person', 'Family')
]
agents.sort(key=lambda x: x[1])
for nid, agent_id, label, birth, death in agents[:5]:
print(f" {agent_id}: {label}")
print(f" Born: {birth or 'N/A'}, Died: {death or 'N/A'}")
# Show sample RecordResource entities
sub_section("Sample RecordResource entities")
record_resources = [
(nid, a.get('recordResourceId', '?'), a.get('title', ''),
a.get('beginningDate', ''), a.get('endDate', ''))
for nid, a in graph._node_attrs.items()
if a.get('main_label') == 'RecordResource'
]
record_resources.sort(key=lambda x: x[1])
for nid, rr_id, title, from_date, to_date in record_resources[:5]:
print(f" {rr_id}: {title or 'No title'}")
print(f" Date range: {from_date or 'N/A'} — {to_date or 'N/A'}")
--- Sample Agent entities ---
agent-agent-000005: No label
Born: 1959-01-08, Died: N/A
agent-agent-000016: No label
Born: 1828-02-10, Died: N/A
agent-agent-000051: No label
Born: 1959-01-01, Died: 1978-12-31
agent-agent-003529: No label
Born: 1945-08-18, Died: 1975-12-31
agent-agent-003530: No label
Born: 1965-01-01, Died: 1975-12-31
--- Sample RecordResource entities ---
recordResource-recordResource-003500-d_1: LUDOVIC VITET (1802-1873)
Date range: N/A — N/A
recordResource-recordResource-003500-d_2: EUGENE AUBRY-VITET (1845-1930) 1
Date range: N/A — N/A
recordResource-recordResource-003500-d_3: FAMILLE COSTA DE BEAUREGARD 1.
Date range: N/A — N/A
recordResource-recordResource-003500-d_4: COLLECTION DE PHOTOGRAPHIES.
Date range: N/A — N/A
recordResource-recordResource-007375-d_1: FRANCE
Date range: N/A — N/A
Step 8: Summary statistics
Show the final state of the RiC-O graph.
[11]:
section("Final summary")
# Nodes by label
sub_section("Nodes by label")
label_counts = Counter(a.get('main_label', 'Node') for a in graph._node_attrs.values())
for label, count in label_counts.most_common():
print(f" {label}: {count}")
# Propagation edges
sub_section("Propagation edges by type")
prop_rel_counts = Counter()
for u, v, k, d in graph._graph.edges(data=True, keys=True):
edge_attrs = graph.get_edge_attrs(u, v, d.get('rel_type', k))
if edge_attrs.get('_propagate') is True:
rel_type = d.get('rel_type', k) or edge_attrs.get('_propagate', False) and 'parent' or 'unknown'
prop_rel_counts[rel_type] += 1
for rel_type, count in prop_rel_counts.most_common():
print(f" {rel_type}: {count}")
# All Things
sub_section("All Things")
for nid, identifier, name in things:
print(f" {identifier}: {name}")
# Subdocument counts
sub_section("Subdocuments per Thing")
for nid, identifier, name in things:
thing_node = Thing(pk={"identifier": identifier})
subdocs = graph.get_subdocuments(thing_node)
print(f" {identifier}: {len(subdocs)} subdocuments")
# Persistence file info
sub_section("Persistence file")
print(f" Path: {_persistence_file}")
if os.path.exists(_persistence_file):
size = os.path.getsize(_persistence_file)
print(f" Size: {size:,} bytes ({size / 1024:.1f} KB)")
else:
print(" File not found (data only in memory)")
print("\n" + "=" * 60)
print(" ✅ RiC-O NetworkX demo completed successfully!")
print("=" * 60)
============================================================
Final summary
============================================================
--- Nodes by label ---
AgentName: 48
Thing: 40
RecordResource: 27
Instantiation: 27
Agent: 10
--- Propagation edges by type ---
HAS_AGENTNAME: 48
HAS_RECORDRESOURCE: 27
HAS_INSTANTIATION: 27
HAS_AGENT: 10
--- All Things ---
record-000005: record-000005
record-000016: record-000016
record-000051: record-000051
record-003500: record-003500
record-003529: record-003529
record-003530: record-003530
record-003531: record-003531
record-003532: record-003532
record-003549: record-003549
record-003550: record-003550
record-003551: record-003551
record-007375: record-007375
record-009555: record-009555
thing-rr-recordResource-recordResource-003500-d_1: thing-rr-recordResource-recordResource-003500-d_1
thing-rr-recordResource-recordResource-003500-d_2: thing-rr-recordResource-recordResource-003500-d_2
thing-rr-recordResource-recordResource-003500-d_3: thing-rr-recordResource-recordResource-003500-d_3
thing-rr-recordResource-recordResource-003500-d_4: thing-rr-recordResource-recordResource-003500-d_4
thing-rr-recordResource-recordResource-007375-d_1: thing-rr-recordResource-recordResource-007375-d_1
thing-rr-recordResource-recordResource-007375-d_2: thing-rr-recordResource-recordResource-007375-d_2
thing-rr-recordResource-recordResource-009555-d_1: thing-rr-recordResource-recordResource-009555-d_1
thing-rr-recordResource-recordResource-009555-d_10: thing-rr-recordResource-recordResource-009555-d_10
thing-rr-recordResource-recordResource-009555-d_11: thing-rr-recordResource-recordResource-009555-d_11
thing-rr-recordResource-recordResource-009555-d_12: thing-rr-recordResource-recordResource-009555-d_12
thing-rr-recordResource-recordResource-009555-d_13: thing-rr-recordResource-recordResource-009555-d_13
thing-rr-recordResource-recordResource-009555-d_14: thing-rr-recordResource-recordResource-009555-d_14
thing-rr-recordResource-recordResource-009555-d_15: thing-rr-recordResource-recordResource-009555-d_15
thing-rr-recordResource-recordResource-009555-d_16: thing-rr-recordResource-recordResource-009555-d_16
thing-rr-recordResource-recordResource-009555-d_17: thing-rr-recordResource-recordResource-009555-d_17
thing-rr-recordResource-recordResource-009555-d_18: thing-rr-recordResource-recordResource-009555-d_18
thing-rr-recordResource-recordResource-009555-d_2: thing-rr-recordResource-recordResource-009555-d_2
thing-rr-recordResource-recordResource-009555-d_3: thing-rr-recordResource-recordResource-009555-d_3
thing-rr-recordResource-recordResource-009555-d_4: thing-rr-recordResource-recordResource-009555-d_4
thing-rr-recordResource-recordResource-009555-d_5: thing-rr-recordResource-recordResource-009555-d_5
thing-rr-recordResource-recordResource-009555-d_6: thing-rr-recordResource-recordResource-009555-d_6
thing-rr-recordResource-recordResource-009555-d_7: thing-rr-recordResource-recordResource-009555-d_7
thing-rr-recordResource-recordResource-009555-d_8: thing-rr-recordResource-recordResource-009555-d_8
thing-rr-recordResource-recordResource-009555-d_9: thing-rr-recordResource-recordResource-009555-d_9
thing-rr-recordResource-recordResource-top-003500: thing-rr-recordResource-recordResource-top-003500
thing-rr-recordResource-recordResource-top-007375: thing-rr-recordResource-recordResource-top-007375
thing-rr-recordResource-recordResource-top-009555: thing-rr-recordResource-recordResource-top-009555
--- Subdocuments per Thing ---
record-000005: 13 subdocuments
record-000016: 11 subdocuments
record-000051: 4 subdocuments
record-003500: 0 subdocuments
record-003529: 8 subdocuments
record-003530: 4 subdocuments
record-003531: 7 subdocuments
record-003532: 4 subdocuments
record-003549: 2 subdocuments
record-003550: 2 subdocuments
record-003551: 3 subdocuments
record-007375: 0 subdocuments
record-009555: 0 subdocuments
thing-rr-recordResource-recordResource-003500-d_1: 2 subdocuments
thing-rr-recordResource-recordResource-003500-d_2: 2 subdocuments
thing-rr-recordResource-recordResource-003500-d_3: 2 subdocuments
thing-rr-recordResource-recordResource-003500-d_4: 2 subdocuments
thing-rr-recordResource-recordResource-007375-d_1: 2 subdocuments
thing-rr-recordResource-recordResource-007375-d_2: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_1: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_10: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_11: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_12: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_13: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_14: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_15: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_16: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_17: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_18: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_2: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_3: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_4: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_5: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_6: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_7: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_8: 2 subdocuments
thing-rr-recordResource-recordResource-009555-d_9: 2 subdocuments
thing-rr-recordResource-recordResource-top-003500: 2 subdocuments
thing-rr-recordResource-recordResource-top-007375: 2 subdocuments
thing-rr-recordResource-recordResource-top-009555: 2 subdocuments
--- Persistence file ---
Path: /var/folders/rm/5_s35r9j3198mxxpvlpfx8700000gn/T/ric_o_networkx_e073bd7a.pkl
Size: 60,780 bytes (59.4 KB)
============================================================
✅ RiC-O NetworkX demo completed successfully!
============================================================
Cleanup
Close the graph and optionally delete the persistence file.
[12]:
graph.close()
print("✅ NetworkXGraph closed.")
# Clean up persistence file
if os.path.exists(_persistence_file):
os.remove(_persistence_file)
print(f"✅ Persistence file deleted: {_persistence_file}")
else:
print(f"ℹ️ No persistence file to clean up")
✅ NetworkXGraph closed.
✅ Persistence file deleted: /var/folders/rm/5_s35r9j3198mxxpvlpfx8700000gn/T/ric_o_networkx_e073bd7a.pkl