#!/usr/bin/env python # coding: utf-8 # This notebooks is hosted on [codeberg](https://codeberg.org/cknoll/kram/src/branch/main/python-stuff/mastodon-discourse-visualization). It demonstrates how to generate the following toot-relation-graph. # # In[1]: from IPython.display import SVG, display, HTML; svg_fname = "toot-analysis-107742285383682464.svg"; svgfile = open(svg_fname, "r"); svg_data = svgfile.read(); svgfile.close(); display(SVG(svg_data)) # This notebook depends on some packages and files which are not part of this repo: # # - https://mastodonpy.readthedocs.io/en/stable/index.html# # - https://networkx.org/documentation/stable/reference/drawing.html # - https://github.com/twosigma/nxv # - https://codeberg.org/cknoll/cachewrapper (optional) # # # Install these dependencies by # ``` # pip install mastodonpy networkx nxv # # ``` # In[2]: import time print(time.ctime()) # https://github.com/cknoll/ipydex (optional) get_ipython().run_line_magic('load_ext', 'ipydex.displaytools') # In[3]: from mastodon import Mastodon # https://mastodonpy.readthedocs.io/en/stable/index.html# import networkx as nx # https://networkx.org/documentation/stable/reference/drawing.html import nxv # https://github.com/twosigma/nxv from matplotlib import pyplot as plt try: import cachewrapper as cw # https://codeberg.org/cknoll/cachewrapper (optional) except ImportError: cw = None from IPython.display import SVG, display, HTML masto_url = "https://social.tchncs.de" cache_path = "cache.pcl" # mc means "mastodon-client" mc = Mastodon( access_token = 'pymastoclient_usercred.secret', api_base_url = masto_url, ) # this is optional (to save api-calls) if 0 and cw: mc = cw.CacheWrapper(mc) mc.load_cache(cache_path) # id taken from local url (click on `···`→ expand this post (in the toot) ) # 107742285383682464 root_id = "107742285383682464" res = mc.status_context(root_id) ##:i #mc.save_cache(cache_path) ancestors = res["ancestors"] ##:i descendants = res["descendants"] ##:i # In[4]: res.keys() # In[5]: # ancestors # In[6]: # instantiate the default color_cycler from matlotlib color_cycler = plt.rcParamsDefault['axes.prop_cycle']() # next(color_cycler) -> {'color': '#2ca02c'} etc # In[7]: author_color_map = {} # In[8]: def get_author_color(data): aid = data["account"]["id"] color = author_color_map.get(aid) if color: return color else: # get new color from cycler, save it, return it color = next(color_cycler)["color"] author_color_map[aid] = color return color # In[9]: class Node(object): def __init__(self, data: dict): self.id = str(data["id"]) self.label = self.id self.data = data self.title= "foo" author = data["account"]["display_name"] N = 10 if len(author) > N: author = f"{author[:N-2]}…" # label-trick: generate strings like r'{K12345}' # these can be substituted later -> two keys for linebreak self.repr_str = f"{author}:\n{{K{self.id}}}\n{{X{self.id}}}" self.style_dict = { "color": get_author_color(data), "fillcolor": get_author_color(data)+"20", # add low alpha-value for transparency "style": "filled", "shape": "circle", "fixedsize": True, "width": 0.7, "fontsize": 8, "penwidth": 2, } def __repr__(self): return self.repr_str # In[10]: G = nx.DiGraph() root_node = Node(data=mc.status(root_id)) id_map = {root_id: root_node} G.add_node(root_node) parent = root_node for obj_data in descendants: node = Node(obj_data) id_map[node.id] = node parent = id_map[str(node.data["in_reply_to_id"])] G.add_node(node, color="red", shape="pentagon") G.add_edge(parent, node) # In[11]: nn = list(G.nodes)[0] # In[12]: import bleach # In[13]: bleach.clean(nn.data["content"], tags=[], strip=True) # In[14]: #