Skip to content

PROV-JSON

Export a KnowledgeGraph to, and import it from, PROV-JSON.

PROV-JSON <https://www.w3.org/submissions/prov-json/>_ is a standard JSON serialisation of the W3C PROV data model <https://www.w3.org/TR/prov-dm/>_. The generic mapping is:

  • a node of type activity -> prov:Activity; type agent -> prov:Agent; any other type -> prov:Entity;
  • a node's type becomes prov:type (as a ck: qualified name), its text and each property become ck: attributes;
  • edges map to the matching PROV relation (used / generated_by / derived_from / attributed_to); every other edge type has no PROV equivalent and is exported as wasInfluencedBy carrying a ck:predicate attribute so the original relationship is preserved.

Import is best-effort and lossy (node ids/types/text/properties survive, but node timestamps are not carried by PROV); the stable invariant is that re-exporting an imported graph reproduces the document.

dumps_prov(graph, *, indent=2)

Serialise a knowledge graph to a PROV-JSON string.

Parameters:

Name Type Description Default
graph KnowledgeGraph

The graph to export.

required
indent int

Indentation passed to :func:json.dumps.

2

Returns:

Type Description
str

The PROV-JSON document as a string.

Source code in src/ideagraph/kg/prov.py
141
142
143
144
145
146
147
148
149
150
151
152
def dumps_prov(graph: KnowledgeGraph, *, indent: int = 2) -> str:
    """Serialise a knowledge graph to a PROV-JSON string.

    Args:
        graph: The graph to export.
        indent: Indentation passed to :func:`json.dumps`.

    Returns:
        The PROV-JSON document as a string.

    """
    return json.dumps(to_prov(graph), indent=indent, ensure_ascii=False)

from_prov(document)

Reconstruct a knowledge graph from a PROV-JSON document.

Parameters:

Name Type Description Default
document dict[str, Any]

A PROV-JSON document as produced by :func:to_prov.

required

Returns:

Type Description
KnowledgeGraph

The reconstructed graph. External endpoint stubs (global ids or the

KnowledgeGraph

generic ck:external / ck:node placeholders) are not materialised

KnowledgeGraph

as nodes.

Source code in src/ideagraph/kg/prov.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def from_prov(document: dict[str, Any]) -> KnowledgeGraph:
    """Reconstruct a knowledge graph from a PROV-JSON document.

    Args:
        document: A PROV-JSON document as produced by :func:`to_prov`.

    Returns:
        The reconstructed graph. External endpoint stubs (global ids or the
        generic ``ck:external`` / ``ck:node`` placeholders) are not materialised
        as nodes.

    """
    graph = KnowledgeGraph()
    for bucket in ("entity", "activity", "agent"):
        for qn, attrs in document.get(bucket, {}).items():
            local = _local(qn)
            if is_global_id(local) or attrs.get("prov:type") in ("ck:external", "ck:node"):
                continue
            graph.add_node(_node_from_attrs(qn, attrs))

    for name, payload_map in document.items():
        if name in _RELATION_ROLES_REV:
            etype, s_role, o_role = _RELATION_ROLES_REV[name]
            for qn, payload in payload_map.items():
                graph.add_edge(
                    Edge(type=etype, source=_local(payload[s_role]), target=_local(payload[o_role]), id=_local(qn))
                )
        elif name == "wasInfluencedBy":
            for qn, payload in payload_map.items():
                graph.add_edge(
                    Edge(
                        type=payload["ck:predicate"],
                        source=_local(payload["prov:influencee"]),
                        target=_local(payload["prov:influencer"]),
                        id=_local(qn),
                    )
                )
    return graph

loads_prov(text)

Deserialise a knowledge graph from a PROV-JSON string.

Parameters:

Name Type Description Default
text str

A PROV-JSON document as produced by :func:dumps_prov.

required

Returns:

Type Description
KnowledgeGraph

The reconstructed graph.

Source code in src/ideagraph/kg/prov.py
220
221
222
223
224
225
226
227
228
229
230
def loads_prov(text: str) -> KnowledgeGraph:
    """Deserialise a knowledge graph from a PROV-JSON string.

    Args:
        text: A PROV-JSON document as produced by :func:`dumps_prov`.

    Returns:
        The reconstructed graph.

    """
    return from_prov(json.loads(text))

to_prov(graph)

Convert a knowledge graph to a PROV-JSON document.

Parameters:

Name Type Description Default
graph KnowledgeGraph

The graph to export.

required

Returns:

Type Description
dict[str, Any]

A PROV-JSON document as a plain dictionary.

Source code in src/ideagraph/kg/prov.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def to_prov(graph: KnowledgeGraph) -> dict[str, Any]:
    """Convert a knowledge graph to a PROV-JSON document.

    Args:
        graph: The graph to export.

    Returns:
        A PROV-JSON document as a plain dictionary.

    """
    entity: dict[str, Any] = {}
    activity: dict[str, Any] = {}
    agent: dict[str, Any] = {}
    buckets = {_ACTIVITY: activity, _AGENT: agent}

    for node in graph.nodes.values():
        buckets.get(node.type, entity)[_qn(node.id)] = _node_attrs(node)

    def _ensure_entity(node_id: str) -> None:
        qn = _qn(node_id)
        if qn not in entity and qn not in activity and qn not in agent:
            entity[qn] = {"prov:type": "ck:external"} if is_global_id(node_id) else {"prov:type": "ck:node"}

    relations: dict[str, dict[str, Any]] = {
        name: {} for name in ("used", "wasGeneratedBy", "wasDerivedFrom", "wasAttributedTo", "wasInfluencedBy")
    }
    for edge in graph.edges.values():
        _ensure_entity(edge.source)
        _ensure_entity(edge.target)
        subject, obj = _qn(edge.source), _qn(edge.target)
        roles = _RELATION_ROLES.get(edge.type)
        if roles is not None:
            name, s_role, o_role = roles
            relations[name][_qn(edge.id)] = {s_role: subject, o_role: obj}
        else:
            relations["wasInfluencedBy"][_qn(edge.id)] = {
                "prov:influencee": subject,
                "prov:influencer": obj,
                "ck:predicate": edge.type,
            }

    document: dict[str, Any] = {"prefix": {"ck": CK_NAMESPACE}}
    for name, collection in (("entity", entity), ("activity", activity), ("agent", agent), *relations.items()):
        if collection:
            document[name] = collection
    return document