Many text based conversations threads can be visualised as a tree. This is a follow-up to yesterday's blog post about Twitter conversation trees.
Mailing list archives often use nested
<ul> to show a conversation.
That's fine, but has the major drawback of not being interactive. There's no way to collapse a branch of a tree if you're not interested in that strand of the conversation.
Older readers may be familiar with how USENET displayed threads:
Younger readers are probably familiar with how Reddit displays threads:
This is the summary. Click me!
I am the content inside the detail. Click the summary to close.
The code for that is:
<details> <summary>This is the summary. Click me!</summary> <p>I am the content inside the detail. Click the summary to close.</p> </details>
So, we can nest
<details> and make an interactive tree!
WARNING! This is a misuse of these HTML elements. While it is valid markup, it may not be accessible. This is an experiment and not intended for production use.
What's your favourite Pizza topping? (2 replies)
I like pineapple (1 reply)
What's wrong with you? (0 replies)
Sweetcorn is my favourite! (0 replies)
And the code is:
<details> <summary>Top level message (2 replies)</summary> <details> <summary>2nd level message (1 reply)</summary> <details open> <summary>3rd level message (0 replies)</summary> </details> </details> <details open> <summary>2nd level message (0 replies)</summary> </details> </details>
Python has a number of Tree structure data-types. I use TreeLib.
Here's how to create a Tree. This assumes that every Node has its own ID and references the ID of its parent.
from treelib import Node, Tree tree = Tree() tree.create_node("root", "1") # root node tree.create_node("reply", "2", parent="1") tree.create_node("deeper", "3", parent="2") tree.create_node("much deeper", "4", parent="3") tree.create_node("another", "5", parent="2") tree.create_node("top level reply", "6", parent="1") tree.show()
This is what the Tree looks like:
root ├── reply │ ├── another │ └── deeper │ └── much deeper └── top level reply
Here's how to walk / traverse the tree and output nested HTML. (I'm sure there are more efficient ways of doing this!)
First, get a list of nodes,
# Ordered list of nodes by depth nodeList = list(tree.expand_tree(mode=Tree.DEPTH))
Keep track of which depth we're at
# Keep track of which depth we're at previousDepth = 0 # Set up the output htmlOutput = "" # Traverse the tree for n in nodeList: # How deep are we? currentDepth = tree.depth(n) # Text to display messageText = tree.get_node(n).tag # How many *direct* children of this node? numberOfChildren = len(tree.children(n)) # Pluralise the (number of replies) text replyText = "reply" if numberOfChildren == 1 else "replies" # If this is the root level, there's nothing to do if (currentDepth == 0): pass # If this is the same level as the previous message, keep the <details> element unclosed elif (currentDepth == previousDepth): htmlOutput += "\n" # If we're less deep, we need to close at least one <details> elif (currentDepth < previousDepth): for x in range(0, (previousDepth-currentDepth)): htmlOutput += "</details>" # Add the number of replies if (numberOfChildren == 0): # Set the details as open htmlOutput += "<details open><summary>" + messageText + "</summary></details>" else: htmlOutput += "<details><summary>" + messageText + " (" + str(numberOfChildren) + " "+replyText+")</summary>" # All done, reset the depth previousDepth = currentDepth htmlOutput +="</details>" print(htmlOutput)
I doubt this is the best, or most Pythonic, way to do it. But it works!