You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

138 lines
3.7 KiB

  1. import sqlite3
  2. import pydot
  3. import cgi
  4. import json
  5. import datetime
  6. import argparse
  7. import urllib2
  8. def make_name(pdu_id, origin):
  9. return "%s@%s" % (pdu_id, origin)
  10. def make_graph(pdus, room, filename_prefix):
  11. pdu_map = {}
  12. node_map = {}
  13. origins = set()
  14. colors = set(("red", "green", "blue", "yellow", "purple"))
  15. for pdu in pdus:
  16. origins.add(pdu.get("origin"))
  17. color_map = {color: color for color in colors if color in origins}
  18. colors -= set(color_map.values())
  19. color_map[None] = "black"
  20. for o in origins:
  21. if o in color_map:
  22. continue
  23. try:
  24. c = colors.pop()
  25. color_map[o] = c
  26. except:
  27. print "Run out of colours!"
  28. color_map[o] = "black"
  29. graph = pydot.Dot(graph_name="Test")
  30. for pdu in pdus:
  31. name = make_name(pdu.get("pdu_id"), pdu.get("origin"))
  32. pdu_map[name] = pdu
  33. t = datetime.datetime.fromtimestamp(
  34. float(pdu["ts"]) / 1000
  35. ).strftime('%Y-%m-%d %H:%M:%S,%f')
  36. label = (
  37. "<"
  38. "<b>%(name)s </b><br/>"
  39. "Type: <b>%(type)s </b><br/>"
  40. "State key: <b>%(state_key)s </b><br/>"
  41. "Content: <b>%(content)s </b><br/>"
  42. "Time: <b>%(time)s </b><br/>"
  43. "Depth: <b>%(depth)s </b><br/>"
  44. ">"
  45. ) % {
  46. "name": name,
  47. "type": pdu.get("pdu_type"),
  48. "state_key": pdu.get("state_key"),
  49. "content": cgi.escape(json.dumps(pdu.get("content")), quote=True),
  50. "time": t,
  51. "depth": pdu.get("depth"),
  52. }
  53. node = pydot.Node(
  54. name=name,
  55. label=label,
  56. color=color_map[pdu.get("origin")]
  57. )
  58. node_map[name] = node
  59. graph.add_node(node)
  60. for pdu in pdus:
  61. start_name = make_name(pdu.get("pdu_id"), pdu.get("origin"))
  62. for i, o in pdu.get("prev_pdus", []):
  63. end_name = make_name(i, o)
  64. if end_name not in node_map:
  65. print "%s not in nodes" % end_name
  66. continue
  67. edge = pydot.Edge(node_map[start_name], node_map[end_name])
  68. graph.add_edge(edge)
  69. # Add prev_state edges, if they exist
  70. if pdu.get("prev_state_id") and pdu.get("prev_state_origin"):
  71. prev_state_name = make_name(
  72. pdu.get("prev_state_id"), pdu.get("prev_state_origin")
  73. )
  74. if prev_state_name in node_map:
  75. state_edge = pydot.Edge(
  76. node_map[start_name], node_map[prev_state_name],
  77. style='dotted'
  78. )
  79. graph.add_edge(state_edge)
  80. graph.write('%s.dot' % filename_prefix, format='raw', prog='dot')
  81. graph.write_png("%s.png" % filename_prefix, prog='dot')
  82. graph.write_svg("%s.svg" % filename_prefix, prog='dot')
  83. def get_pdus(host, room):
  84. transaction = json.loads(
  85. urllib2.urlopen(
  86. "http://%s/context/%s/" % (host, room)
  87. ).read()
  88. )
  89. return transaction["pdus"]
  90. if __name__ == "__main__":
  91. parser = argparse.ArgumentParser(
  92. description="Generate a PDU graph for a given room by talking "
  93. "to the given homeserver to get the list of PDUs. \n"
  94. "Requires pydot."
  95. )
  96. parser.add_argument(
  97. "-p", "--prefix", dest="prefix",
  98. help="String to prefix output files with"
  99. )
  100. parser.add_argument('host')
  101. parser.add_argument('room')
  102. args = parser.parse_args()
  103. host = args.host
  104. room = args.room
  105. prefix = args.prefix if args.prefix else "%s_graph" % (room)
  106. pdus = get_pdus(host, room)
  107. make_graph(pdus, room, prefix)