@RenTheRoot here is also my version of the script. It takes into account such cases as:
- parents and children nodes and edges
- associated people nodes and edges
- marriages events nodes and edges with people
- shared events nodes and edges with people
But it still has issues. Currently I receive such graph on a small test DB:
Maybe you will use also some my ideas for your future export addon. What does not work here:
- nodes for events and people looks identically, but should have different colors
- at least shared events should be turned on/off via setting because some users could have events with a lot of people, like âCensusâ.
- spouses arenot aligned horizontally. And I think Spouces with their Marriage event should be a grouped.
# Import required libraries
import json
import graphviz
# Define constants for line colors and styles
SPOUSE_EDGE_COLOR = '#005000' # Black
SPOUSE_EDGE_STYLE = 'bold'
CHILD_EDGE_COLOR = '#00FF00' # Green
CHILD_EDGE_STYLE = 'bold'
ASSOCIATION_EDGE_COLOR = '#0000FF' # Blue
ASSOCIATION_EDGE_STYLE = 'dotted'
SHARED_EVENT_EDGE_STYLE = '#FF0000' # Red
SHARED_EVENT_EDGE_COLOR = 'dashed'
# Define functions to process components from JSON data
# Function to parse JSON lines into components
def getAllComponents(lines):
components = []
for line in lines:
components.append(json.loads(line))
print("components count: ", len(components))
return components
# Function to extract people from components
def getAllPeople(components):
people = {i["handle"]: i for i in components if i["_class"] == "Person"}
print("People count: ", len(people))
return people
# Function to extract events from components
def getAllEvents(components):
events = {i["handle"]: i for i in components if i["_class"]=="Event"}
print("Events count: ", len(events))
return events
# Function to extract families from components
def getAllFamilies(components):
families = {i["handle"]: i for i in components if i["_class"]=="Family"}
print("Families count: ", len(families))
return families
# Function to get the full name of a person
def getFullname(person):
primaryName = person.get("primary_name")
firstName = primaryName.get("first_name")
surnameList = primaryName.get("surname_list")
surnamesStr = " ".join([i.get("surname") for i in surnameList])
fullnameStr = f"{firstName} {surnamesStr}"
return fullnameStr
# Function to get the name of an event
def getEventName(eventHandle):
eventName = "Event"
event = events[eventHandle]
eventType = event.get("type")
if eventType and eventType.get("string"):
eventName = eventType.get("string")
return eventName
# Function to get the Gramps ID of an event
def getEventGrampsId(eventHandle):
event = events[eventHandle]
return event.get("gramps_id")
# Function to extract shared events from people's event references
def getSharedEvents():
sharedEvents = {}
for personHandle, person in people.items():
eventRefList = person.get("event_ref_list", [])
for eventRef in eventRefList:
eventHandle = eventRef.get("ref")
if eventHandle not in events:
continue
eventName = getEventName(eventHandle)
eventGrampsId = getEventGrampsId(eventHandle)
if eventHandle not in sharedEvents:
sharedEvents[eventHandle] = {"gramps_id": eventGrampsId, "event_name": eventName, "people_handles": [personHandle]}
else:
sharedEvents[eventHandle]["people_handles"].append(personHandle)
print("Shared Events count: ", len(sharedEvents))
return sharedEvents
# Function to extract marriage events from all events
def getMarriageEvents():
marriageEvents = {}
for handle, event in events.items():
if getEventName(handle) != 'Marriage':
continue
marriageEvents[handle] = event
return marriageEvents
# Read JSON file and parse its contents into components
with open('Untitled_2.json', 'r',encoding='utf-8') as f:
lines = f.readlines()
components = getAllComponents(lines)
# Extract people, events, families, marriage events, and shared events from components
people = getAllPeople(components)
events = getAllEvents(components)
families = getAllFamilies(components)
marriageEvents = getMarriageEvents()
sharedEvents = getSharedEvents()
# Create a Graphviz Digraph object
dot = graphviz.Digraph()
# Add nodes for each person, displaying their full name and Gramps ID
for handle, person in people.items():
fullnameStr = getFullname(person)
grampsId = person.get("gramps_id")
dot.node(handle, f"{grampsId}: {fullnameStr}")
# Add nodes for marriage events
for handle, family in families.items():
eventRefList = family.get("event_ref_list", []);
for eventRef in eventRefList:
eventHandle = eventRef.get("ref")
if not eventHandle or eventHandle not in marriageEvents:
continue
event = marriageEvents.get(eventHandle)
grampsId = event.get("gramps_id")
dot.node(eventHandle, f"{grampsId}: Marriage")
# Add nodes for shared events with at least two people
for eventHandle, sharedEvent in sharedEvents.items():
peopleHandles = sharedEvent.get("people_handles");
if len(peopleHandles) < 2:
continue
eventName = sharedEvent.get("event_name");
if (eventName == "Marriage"):
continue
eventGrampsId = sharedEvent.get("gramps_id")
dot.node(eventHandle, f"{eventGrampsId}: {eventName}")
# Add spouse and their children edges
for handle, family in families.items():
fatherHandle = family.get("father_handle")
motherHandle = family.get("mother_handle")
eventRefList = family.get("event_ref_list")
if not fatherHandle or not motherHandle or fatherHandle not in people or motherHandle not in people:
continue
with dot.subgraph() as s:
s.attr(rank='same')
s.node(fatherHandle)
s.node(motherHandle)
dot.edge(fatherHandle, motherHandle, label="Spouse", dir="both", style=SPOUSE_EDGE_STYLE, color=SPOUSE_EDGE_COLOR)
for eventRef in eventRefList:
eventHandle = eventRef.get("ref")
if not eventHandle or eventHandle not in marriageEvents:
continue
dot.edge(fatherHandle, eventHandle, label="", dir="none", style=SPOUSE_EDGE_STYLE, color=SPOUSE_EDGE_COLOR)
dot.edge(motherHandle, eventHandle, label="", dir="none", style=SPOUSE_EDGE_STYLE, color=SPOUSE_EDGE_COLOR)
childRefList = family.get("child_ref_list", [])
for childRef in childRefList:
childHandle = childRef.get("ref")
if (childHandle and childHandle in people):
dot.edge(eventHandle, childHandle, label="Child", dir="forward", style=CHILD_EDGE_STYLE, color=CHILD_EDGE_COLOR)
# Add association edges between people
for handle, person in people.items():
personRefList = person.get("person_ref_list", []);
for personRef in personRefList:
personRefHandle = personRef.get("ref")
personRefRel = personRef.get("rel")
if (personRefHandle and personRefHandle in people):
dot.edge(handle, personRefHandle, label=personRefRel, dir="forward", style=ASSOCIATION_EDGE_STYLE, color=ASSOCIATION_EDGE_COLOR)
# Add edges from shared events to associated people, labeling the relationship role
for eventHandle, sharedEvent in sharedEvents.items():
peopleHandles = sharedEvent.get("people_handles")
for personHandle in peopleHandles:
if (personHandle not in people):
continue
person = people.get(personHandle)
eventRefList = person.get("event_ref_list")
for eventRef in eventRefList:
ref = eventRef.get("ref")
role = eventRef.get("role")
roleName = role.get("string")
if eventHandle != ref:
continue
dot.edge(eventHandle, personHandle, label=roleName, dir="back", style=SHARED_EVENT_EDGE_STYLE, color=SHARED_EVENT_EDGE_COLOR)
with open("out.gv", 'w') as f:
f.write(dot.source)