https://fperucic.github.io/treant-js/ is about the only Family Tree library that I could find. It has not been frequently used, but it seemed to fit my purposes and I needed to find a way to put family trees on my application so that was good enough. However, I did not find much material of the library applied from Flask so I needed to approach the proposition cautiously.
Firstly, the dichotomy between the backend and frontend is a very established principle. Treant.js uses JSON which is parsed by the Javascript of the library to generate the Family Tree. So apparently the only thing I needed to do was to get the JSON to the frontend and that would be it. After tinkering around, I understood that I needed to go about it in the following steps:
Demonstrate the graphic from Javascript
Demonstrate the graphic by sending JSON from backend
Demonstrate the graphic by generating JSON from database
The first part of demonstrating the graphic from Javascript was relatively simple. On the webpage, I added the css and the js tags.
<link rel="stylesheet" href="{{ url_for('static', filename='css/Treant-1.0/Treant.css') }}" type="text/css"/>
<link rel="stylesheet" href="{{ url_for('static', filename='css/Treant-1.0/basic.css') }}" type="text/css"/>
The first .css is the general Treant.css. The second is used to customize each drawn graph. This means that a different .css is needed for each type of graph that you intend to draw on your project.
Next comes the js tags.
<script src="{{ url_for('static', filename='js/Treant-1.0/vendor/raphael.js') }}" ></script>
<script src="{{ url_for('static', filename='js/Treant-1.0/Treant.js') }}" ></script>
The css tags come in at the top and the js tags at the bottom. Below the above two js tags, I add this bit of code
<script>
var chart_config = {
"chart": {
"container": "#tree-simple"
},
"nodeStructure": {
"text": { "name": "Parent node" },
"children": [{
"text": { "name": "First child" }},
{
"text": { "name": "Second child" }}
]
}
};
var chart = new Treant(chart_config);
</script>
This code executes when the page is displayed. The Treant is called with chart_config. This makes Treant.js work with the data in chart_config and output it in the div with "tree-simple" id. I didn't find much problems with this.
The second part is to reproduce the chart_config JSON on the backend. Suitably process it and then send it to the front end. On the back_end the chart_config information is packed into another datastructure named "display" and the whole passed through Flask to the html document. Using Jinja2, the JSON is reclaimed by
<script>
var js = "{{ display.data | safe }}";
js = js.replace(/'/g,'"');
var obj = JSON.parse(js);
var chart = new Treant(obj);
</script>
Note that the data coming through to the Javascript actually uses single-quotes for the various fields. This is NOT official JSON and the Javascript function JSON.parse() accepts official JSON only i.e. with double-quotes. Hence after the Jinja2 statement, the single-quotes need to be substituted with double-quotes before the JSON parse.
The third part of this requires adding the classes that would come up with the JSON to be sent over. My implementation required
Node
TextData
ChartProperty
Treant
classes. The Node class stored the information that implements each node in the family tree. TextData represents the data e.g. name, to be displayed on the diagram. ChartProperty the graph attributes that define the graph. Finally the Treant class that consolidates chart and node information. The following shows how I define the Node class.
from model.treant.MiscData import TextData
class Node(object):
def __new__(self):
return object.__new__(self)
def __init__(self):
self.text = None
self.children = []
def __repr__(self):
return str(self.__dict__)
def get(self, tree, _id):
for leaf in tree:
if leaf['_id'] == _id:
return leaf
return None
def load(self, tree, _id):
leaf = self.get(tree, _id)
if leaf:
self.text = TextData(leaf['title'])
for child in leaf['children']:
cnode = Node()
cnode.load(tree, child['_id'])
self.children.append(cnode)
Few points of note. tree as used in the get and load functions, is an array that holds the nodes of the information.
Important point, note how the function __repr__ is defined. All the classes related to the JSON structure to be passed must have a defined __repr__function. so that the relevant JSON structure can be built out of the representations. Without the above __repr__ functions, you will get the well-known <.... is not JSON serializable > complaint. This is the way out!
I have another function that generates the tree from whatever underlying database that the system is based on. In my project, I used MongoDB and used aggregate and graphlookup functions to generate the tree.