This file is indexed.

/usr/lib/python3/dist-packages/prov/dot.py is in python3-prov 1.5.0-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
"""Graphical visualisation support for prov.model.

This module produces graphical visualisation for provenanve graphs.
Requires pydotplus module and Graphviz.

References:

* pydotplus homepage: http://pydotplus.readthedocs.io/
* Graphviz:       http://www.graphviz.org/
* DOT Language:   http://www.graphviz.org/doc/info/lang.html

.. moduleauthor:: Trung Dong Huynh <trungdong@donggiang.com>
"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

__author__ = 'Trung Dong Huynh'
__email__ = 'trungdong@donggiang.com'

try:
    from html import escape
except ImportError:
    from cgi import escape
from datetime import datetime
import pydotplus as pydot
import six

from prov.model import (
    PROV_ACTIVITY, PROV_AGENT, PROV_ALTERNATE, PROV_ASSOCIATION,
    PROV_ATTRIBUTION, PROV_BUNDLE, PROV_COMMUNICATION, PROV_DERIVATION,
    PROV_DELEGATION, PROV_ENTITY, PROV_GENERATION, PROV_INFLUENCE,
    PROV_INVALIDATION, PROV_END, PROV_MEMBERSHIP, PROV_MENTION,
    PROV_SPECIALIZATION, PROV_START, PROV_USAGE, Identifier,
    PROV_ATTRIBUTE_QNAMES, sorted_attributes, ProvException
)


# Visual styles for various elements (nodes) and relations (edges)
# see http://graphviz.org/content/attrs
DOT_PROV_STYLE = {
    # Generic node
    0: {
        'shape': 'oval', 'style': 'filled',
        'fillcolor': 'lightgray', 'color': 'dimgray'
    },
    # Elements
    PROV_ENTITY: {
        'shape': 'oval', 'style': 'filled',
        'fillcolor': '#FFFC87', 'color': '#808080'
    },
    PROV_ACTIVITY: {
        'shape': 'box', 'style': 'filled',
        'fillcolor': '#9FB1FC', 'color': '#0000FF'
    },
    PROV_AGENT: {
        'shape': 'house', 'style': 'filled',
        'fillcolor': '#FED37F'
    },
    PROV_BUNDLE: {
        'shape': 'folder', 'style': 'filled',
        'fillcolor': 'aliceblue'
    },
    # Relations
    PROV_GENERATION: {
        'label': 'wasGeneratedBy', 'fontsize': '10.0',
        'color': 'darkgreen', 'fontcolor': 'darkgreen'
    },
    PROV_USAGE: {
        'label': 'used', 'fontsize': '10.0',
        'color': 'red4', 'fontcolor': 'red'
    },
    PROV_COMMUNICATION: {
        'label': 'wasInformedBy', 'fontsize': '10.0'
    },
    PROV_START: {
        'label': 'wasStartedBy', 'fontsize': '10.0'
    },
    PROV_END: {
        'label': 'wasEndedBy', 'fontsize': '10.0'
    },
    PROV_INVALIDATION: {
        'label': 'wasInvalidatedBy', 'fontsize': '10.0'
    },
    PROV_DERIVATION: {
        'label': 'wasDerivedFrom', 'fontsize': '10.0'
    },
    PROV_ATTRIBUTION: {
        'label': 'wasAttributedTo', 'fontsize': '10.0',
        'color': '#FED37F'
    },
    PROV_ASSOCIATION: {
        'label': 'wasAssociatedWith', 'fontsize': '10.0',
        'color': '#FED37F'
    },
    PROV_DELEGATION: {
        'label': 'actedOnBehalfOf', 'fontsize': '10.0',
        'color': '#FED37F'
    },
    PROV_INFLUENCE: {
        'label': 'wasInfluencedBy', 'fontsize': '10.0',
        'color': 'grey'
    },
    PROV_ALTERNATE: {
        'label': 'alternateOf', 'fontsize': '10.0'
    },
    PROV_SPECIALIZATION: {
        'label': 'specializationOf', 'fontsize': '10.0'
    },
    PROV_MENTION: {
        'label': 'mentionOf', 'fontsize': '10.0'
    },
    PROV_MEMBERSHIP: {
        'label': 'hadMember', 'fontsize': '10.0'
    },
    }

ANNOTATION_STYLE = {
    'shape': 'note', 'color': 'gray',
    'fontcolor': 'black', 'fontsize': '10'
}
ANNOTATION_LINK_STYLE = {
    'arrowhead': 'none', 'style': 'dashed',
    'color': 'gray'
}
ANNOTATION_START_ROW = '<<TABLE cellpadding=\"0\" border=\"0\">'
ANNOTATION_ROW_TEMPLATE = """    <TR>
        <TD align=\"left\" href=\"%s\">%s</TD>
        <TD align=\"left\"%s>%s</TD>
    </TR>"""
ANNOTATION_END_ROW = '    </TABLE>>'


def htlm_link_if_uri(value):
    try:
        uri = value.uri
        return '<a href="%s">%s</a>' % (uri, six.text_type(value))
    except AttributeError:
        return six.text_type(value)


def prov_to_dot(bundle, show_nary=True, use_labels=False,
                direction='BT',
                show_element_attributes=True, show_relation_attributes=True):
    """
    Convert a provenance bundle/document into a DOT graphical representation.

    :param bundle: The provenance bundle/document to be converted.
    :type name: :class:`ProvBundle`
    :param show_nary: shows all elements in n-ary relations.
    :type show_nary: bool
    :param use_labels: uses the prov:label property of an element as its name (instead of its identifier).
    :type use_labels: bool
    :param direction: specifies the direction of the graph. Valid values are "BT" (default), "TB", "LR", "RL".
    :param show_element_attributes: shows attributes of elements.
    :type show_element_attributes: bool
    :param show_relation_attributes: shows attributes of relations.
    :type show_relation_attributes: bool
    :returns:  :class:`pydot.Dot` -- the Dot object.
    """
    if direction not in {'BT', 'TB', 'LR', 'RL'}:
        # Invalid direction is provided
        direction = 'BT'  # reset it to the default value
    maindot = pydot.Dot(graph_type='digraph', rankdir=direction, charset='utf-8')

    node_map = {}
    count = [0, 0, 0, 0]  # counters for node ids

    def _bundle_to_dot(dot, bundle):
        def _attach_attribute_annotation(node, record):
            # Adding a node to show all attributes
            attributes = list(
                (attr_name, value) for attr_name, value in record.attributes
                if attr_name not in PROV_ATTRIBUTE_QNAMES
            )

            if not attributes:
                return  # No attribute to display

            # Sort the attributes.
            attributes = sorted_attributes(record.get_type(), attributes)

            ann_rows = [ANNOTATION_START_ROW]
            ann_rows.extend(
                ANNOTATION_ROW_TEMPLATE % (
                    attr.uri, escape(six.text_type(attr)),
                    ' href=\"%s\"' % value.uri if isinstance(value, Identifier)
                    else '',
                    escape(six.text_type(value)
                           if not isinstance(value, datetime) else
                           six.text_type(value.isoformat())))
                for attr, value in attributes
            )
            ann_rows.append(ANNOTATION_END_ROW)
            count[3] += 1
            annotations = pydot.Node(
                'ann%d' % count[3], label='\n'.join(ann_rows),
                **ANNOTATION_STYLE
            )
            dot.add_node(annotations)
            dot.add_edge(pydot.Edge(annotations, node, **ANNOTATION_LINK_STYLE))

        def _add_bundle(bundle):
            count[2] += 1
            subdot = pydot.Cluster(
                graph_name='c%d' % count[2], URL='"%s"' % bundle.identifier.uri
            )
            if use_labels:
                if bundle.label == bundle.identifier:
                    bundle_label = '"%s"' % six.text_type(bundle.label)
                else:
                    # Fancier label if both are different. The label will be
                    # the main node text, whereas the identifier will be a
                    # kind of suptitle.
                    bundle_label = ('<%s<br />'
                                    '<font color="#333333" point-size="10">'
                                    '%s</font>>')
                    bundle_label = bundle_label % (
                        six.text_type(bundle.label),
                        six.text_type(bundle.identifier)
                    )
                subdot.set_label('"%s"' % six.text_type(bundle_label))
            else:
                subdot.set_label('"%s"' % six.text_type(bundle.identifier))
            _bundle_to_dot(subdot, bundle)
            dot.add_subgraph(subdot)
            return subdot

        def _add_node(record):
            count[0] += 1
            node_id = 'n%d' % count[0]
            if use_labels:
                if record.label == record.identifier:
                    node_label = '"%s"' % six.text_type(record.label)
                else:
                    # Fancier label if both are different. The label will be
                    # the main node text, whereas the identifier will be a
                    # kind of suptitle.
                    node_label = ('<%s<br />'
                                  '<font color="#333333" point-size="10">'
                                  '%s</font>>')
                    node_label = node_label % (six.text_type(record.label),
                                               six.text_type(record.identifier))
            else:
                node_label = '"%s"' % six.text_type(record.identifier)

            uri = record.identifier.uri
            style = DOT_PROV_STYLE[record.get_type()]
            node = pydot.Node(
                node_id, label=node_label, URL='"%s"' % uri, **style
            )
            node_map[uri] = node
            dot.add_node(node)

            if show_element_attributes:
                _attach_attribute_annotation(node, rec)
            return node

        def _add_generic_node(qname):
            count[0] += 1
            node_id = 'n%d' % count[0]
            node_label = '"%s"' % six.text_type(qname)

            uri = qname.uri
            style = DOT_PROV_STYLE[0]
            node = pydot.Node(
                node_id, label=node_label, URL='"%s"' % uri, **style
            )
            node_map[uri] = node
            dot.add_node(node)
            return node

        def _get_bnode():
            count[1] += 1
            bnode_id = 'b%d' % count[1]
            bnode = pydot.Node(
                bnode_id, label='""', shape='point', color='gray'
            )
            dot.add_node(bnode)
            return bnode

        def _get_node(qname):
            if qname is None:
                return _get_bnode()
            uri = qname.uri
            if uri not in node_map:
                _add_generic_node(qname)
            return node_map[uri]

        records = bundle.get_records()
        relations = []
        for rec in records:
            if rec.is_element():
                _add_node(rec)
            else:
                # Saving the relations for later processing
                relations.append(rec)

        if not bundle.is_bundle():
            for bundle in bundle.bundles:
                _add_bundle(bundle)

        for rec in relations:
            args = rec.args
            # skipping empty records
            if not args:
                continue
            # picking element nodes
            nodes = [
                value for attr_name, value in rec.formal_attributes
                if attr_name in PROV_ATTRIBUTE_QNAMES
            ]
            other_attributes = [
                (attr_name, value) for attr_name, value in rec.attributes
                if attr_name not in PROV_ATTRIBUTE_QNAMES
            ]
            add_attribute_annotation = (
                show_relation_attributes and other_attributes
            )
            add_nary_elements = len(nodes) > 2 and show_nary
            style = DOT_PROV_STYLE[rec.get_type()]
            if len(nodes) < 2:  # too few elements for a relation?
                continue  # cannot draw this

            if add_nary_elements or add_attribute_annotation:
                # a blank node for n-ary relations or the attribute annotation
                bnode = _get_bnode()

                # the first segment
                dot.add_edge(
                    pydot.Edge(
                        _get_node(nodes[0]), bnode, arrowhead='none', **style
                    )
                )
                style = dict(style)  # copy the style
                del style['label']  # not showing label in the second segment
                # the second segment
                dot.add_edge(pydot.Edge(bnode, _get_node(nodes[1]), **style))
                if add_nary_elements:
                    style['color'] = 'gray'  # all remaining segment to be gray
                    for node in nodes[2:]:
                        if node is not None:
                            dot.add_edge(
                                pydot.Edge(bnode, _get_node(node), **style)
                            )
                if add_attribute_annotation:
                    _attach_attribute_annotation(bnode, rec)
            else:
                # show a simple binary relations with no annotation
                dot.add_edge(
                    pydot.Edge(
                        _get_node(nodes[0]), _get_node(nodes[1]), **style
                    )
                )

    try:
        unified = bundle.unified()
    except ProvException:
        # Could not unify this bundle
        # try the original document anyway
        unified = bundle

    _bundle_to_dot(maindot, unified)
    return maindot