graph2.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. # Copyright 2014-2016 OpenMarket Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import sqlite3
  15. import pydot
  16. import cgi
  17. import json
  18. import datetime
  19. import argparse
  20. from synapse.events import FrozenEvent
  21. from synapse.util.frozenutils import unfreeze
  22. def make_graph(db_name, room_id, file_prefix, limit):
  23. conn = sqlite3.connect(db_name)
  24. sql = (
  25. "SELECT json FROM event_json as j "
  26. "INNER JOIN events as e ON e.event_id = j.event_id "
  27. "WHERE j.room_id = ?"
  28. )
  29. args = [room_id]
  30. if limit:
  31. sql += " ORDER BY topological_ordering DESC, stream_ordering DESC LIMIT ?"
  32. args.append(limit)
  33. c = conn.execute(sql, args)
  34. events = [FrozenEvent(json.loads(e[0])) for e in c.fetchall()]
  35. events.sort(key=lambda e: e.depth)
  36. node_map = {}
  37. state_groups = {}
  38. graph = pydot.Dot(graph_name="Test")
  39. for event in events:
  40. c = conn.execute(
  41. "SELECT state_group FROM event_to_state_groups WHERE event_id = ?",
  42. (event.event_id,),
  43. )
  44. res = c.fetchone()
  45. state_group = res[0] if res else None
  46. if state_group is not None:
  47. state_groups.setdefault(state_group, []).append(event.event_id)
  48. t = datetime.datetime.fromtimestamp(
  49. float(event.origin_server_ts) / 1000
  50. ).strftime("%Y-%m-%d %H:%M:%S,%f")
  51. content = json.dumps(unfreeze(event.get_dict()["content"]))
  52. label = (
  53. "<"
  54. "<b>%(name)s </b><br/>"
  55. "Type: <b>%(type)s </b><br/>"
  56. "State key: <b>%(state_key)s </b><br/>"
  57. "Content: <b>%(content)s </b><br/>"
  58. "Time: <b>%(time)s </b><br/>"
  59. "Depth: <b>%(depth)s </b><br/>"
  60. "State group: %(state_group)s<br/>"
  61. ">"
  62. ) % {
  63. "name": event.event_id,
  64. "type": event.type,
  65. "state_key": event.get("state_key", None),
  66. "content": cgi.escape(content, quote=True),
  67. "time": t,
  68. "depth": event.depth,
  69. "state_group": state_group,
  70. }
  71. node = pydot.Node(name=event.event_id, label=label)
  72. node_map[event.event_id] = node
  73. graph.add_node(node)
  74. for event in events:
  75. for prev_id, _ in event.prev_events:
  76. try:
  77. end_node = node_map[prev_id]
  78. except:
  79. end_node = pydot.Node(name=prev_id, label="<<b>%s</b>>" % (prev_id,))
  80. node_map[prev_id] = end_node
  81. graph.add_node(end_node)
  82. edge = pydot.Edge(node_map[event.event_id], end_node)
  83. graph.add_edge(edge)
  84. for group, event_ids in state_groups.items():
  85. if len(event_ids) <= 1:
  86. continue
  87. cluster = pydot.Cluster(str(group), label="<State Group: %s>" % (str(group),))
  88. for event_id in event_ids:
  89. cluster.add_node(node_map[event_id])
  90. graph.add_subgraph(cluster)
  91. graph.write("%s.dot" % file_prefix, format="raw", prog="dot")
  92. graph.write_svg("%s.svg" % file_prefix, prog="dot")
  93. if __name__ == "__main__":
  94. parser = argparse.ArgumentParser(
  95. description="Generate a PDU graph for a given room by talking "
  96. "to the given homeserver to get the list of PDUs. \n"
  97. "Requires pydot."
  98. )
  99. parser.add_argument(
  100. "-p",
  101. "--prefix",
  102. dest="prefix",
  103. help="String to prefix output files with",
  104. default="graph_output",
  105. )
  106. parser.add_argument("-l", "--limit", help="Only retrieve the last N events.")
  107. parser.add_argument("db")
  108. parser.add_argument("room")
  109. args = parser.parse_args()
  110. make_graph(args.db, args.room, args.prefix, args.limit)