rdfs2dot.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. """
  2. A commandline tool for drawing RDFS Class diagrams in Graphviz DOT
  3. format
  4. You can draw the graph of an RDFS file directly:
  5. ```bash
  6. rdf2dot my_rdfs_file.rdf | dot -Tpng | display
  7. ```
  8. """
  9. from __future__ import annotations
  10. import collections
  11. import itertools
  12. import sys
  13. from typing import Dict
  14. import rdflib.extras.cmdlineutils
  15. from rdflib import RDF, RDFS, XSD
  16. from rdflib.term import Identifier
  17. XSDTERMS = [
  18. XSD[x]
  19. for x in (
  20. "anyURI",
  21. "base64Binary",
  22. "boolean",
  23. "byte",
  24. "date",
  25. "dateTime",
  26. "decimal",
  27. "double",
  28. "duration",
  29. "float",
  30. "gDay",
  31. "gMonth",
  32. "gMonthDay",
  33. "gYear",
  34. "gYearMonth",
  35. "hexBinary",
  36. "ID",
  37. "IDREF",
  38. "IDREFS",
  39. "int",
  40. "integer",
  41. "language",
  42. "long",
  43. "Name",
  44. "NCName",
  45. "negativeInteger",
  46. "NMTOKEN",
  47. "NMTOKENS",
  48. "nonNegativeInteger",
  49. "nonPositiveInteger",
  50. "normalizedString",
  51. "positiveInteger",
  52. "QName",
  53. "short",
  54. "string",
  55. "time",
  56. "token",
  57. "unsignedByte",
  58. "unsignedInt",
  59. "unsignedLong",
  60. "unsignedShort",
  61. )
  62. ]
  63. EDGECOLOR = "blue"
  64. NODECOLOR = "black"
  65. ISACOLOR = "black"
  66. def rdfs2dot(g, stream, opts={}):
  67. """
  68. Convert the RDFS schema in a graph
  69. writes the dot output to the stream
  70. """
  71. fields = collections.defaultdict(set)
  72. nodes: Dict[Identifier, str] = {}
  73. def node(nd):
  74. if nd not in nodes:
  75. nodes[nd] = "node%d" % len(nodes)
  76. return nodes[nd]
  77. def label(xx, grf):
  78. lbl = grf.value(xx, RDFS.label)
  79. if lbl is None:
  80. try:
  81. lbl = grf.namespace_manager.compute_qname(xx)[2]
  82. except Exception:
  83. pass # bnodes and some weird URIs cannot be split
  84. return lbl
  85. stream.write('digraph { \n node [ fontname="DejaVu Sans" ] ; \n')
  86. for x in g.subjects(RDF.type, RDFS.Class):
  87. n = node(x)
  88. for x, y in g.subject_objects(RDFS.subClassOf):
  89. x = node(x)
  90. y = node(y)
  91. stream.write("\t%s -> %s [ color=%s ] ;\n" % (y, x, ISACOLOR))
  92. for x in g.subjects(RDF.type, RDF.Property):
  93. for a, b in itertools.product(
  94. g.objects(x, RDFS.domain), g.objects(x, RDFS.range)
  95. ):
  96. if b in XSDTERMS or b == RDFS.Literal:
  97. l_ = label(b, g)
  98. if b == RDFS.Literal:
  99. l_ = "literal"
  100. fields[node(a)].add((label(x, g), l_))
  101. else:
  102. # if a in nodes and b in nodes:
  103. stream.write(
  104. '\t%s -> %s [ color=%s, label="%s" ];\n'
  105. % (node(a), node(b), EDGECOLOR, label(x, g))
  106. )
  107. for u, n in nodes.items():
  108. stream.write("# %s %s\n" % (u, n))
  109. f = [
  110. "<tr><td align='left'>%s</td><td>%s</td></tr>" % x
  111. for x in sorted(fields[n])
  112. ]
  113. opstr = (
  114. "%s [ shape=none, color=%s label=< <table color='#666666'"
  115. + " cellborder='0' cellspacing='0' border='1'><tr>"
  116. + "<td colspan='2' bgcolor='grey'><B>%s</B></td>"
  117. + "</tr>%s</table> > ] \n"
  118. )
  119. stream.write(opstr % (n, NODECOLOR, label(u, g), "".join(f)))
  120. stream.write("}\n")
  121. def _help():
  122. sys.stderr.write(
  123. """
  124. rdfs2dot.py [-f <format>] files...
  125. Read RDF files given on STDOUT, writes a graph of the RDFS schema in
  126. DOT language to stdout
  127. -f specifies parser to use, if not given,
  128. """
  129. )
  130. def main():
  131. rdflib.extras.cmdlineutils.main(rdfs2dot, _help)
  132. if __name__ == "__main__":
  133. main()