def process_needtables(app, doctree, fromdocname):
    """
    Replace all needtables nodes with a tale of filtered noded.

    :param app:
    :param doctree:
    :param fromdocname:
    :return:
    """
    env = app.builder.env

    for node in doctree.traverse(Needtable):
        if not app.config.needs_include_needs:
            # Ok, this is really dirty.
            # If we replace a node, docutils checks, if it will not lose any attributes.
            # But this is here the case, because we are using the attribute "ids" of a node.
            # However, I do not understand, why losing an attribute is such a big deal, so we delete everything
            # before docutils claims about it.
            for att in ('ids', 'names', 'classes', 'dupnames'):
                node[att] = []
            node.replace_self([])
            continue

        id = node.attributes["ids"][0]
        current_needtable = env.need_all_needtables[id]
        all_needs = env.needs_all_needs

        if current_needtable["style"] == "" or current_needtable["style"].upper() not in ["TABLE", "DATATABLES"]:
            if app.config.needs_table_style == "":
                style = "DATATABLES"
            else:
                style = app.config.needs_table_style.upper()
        else:
            style = current_needtable["style"].upper()

        # Prepare table
        classes = ["NEEDS_{style}".format(style=style)]
        content = nodes.table(classes=classes)
        tgroup = nodes.tgroup()

        # Define Table column width
        # ToDo: Find a way to chosen to perfect width automatically.
        for col in current_needtable["columns"]:
            if col == "TITLE":
                tgroup += nodes.colspec(colwidth=15)
            else:
                tgroup += nodes.colspec(colwidth=5)

        node_columns = []
        for col in current_needtable["columns"]:
            header_name = col.title() if col != "ID" else col
            header_name = header_name.replace("_", " ")
            node_columns.append(nodes.entry('', nodes.paragraph('', header_name)))

        tgroup += nodes.thead('', nodes.row(
            '', *node_columns))
        tbody = nodes.tbody()
        tgroup += tbody
        content += tgroup

        all_needs = list(all_needs.values())

        if current_needtable["sort_by"] is not None:
            if current_needtable["sort_by"] == "id":
                all_needs = sorted(all_needs, key=lambda node: node["id"])
            elif current_needtable["sort_by"] == "status":
                all_needs = sorted(all_needs, key=status_sorter)

        # Perform filtering of needs
        found_needs = procces_filters(all_needs, current_needtable)

        for need_info in found_needs:
            temp_need = need_info.copy()
            if temp_need['is_need']:
                row = nodes.row(classes=['need'])
                prefix = ''
            else:
                row = nodes.row(classes=['need_part'])
                temp_need['id'] = temp_need['id_complete']
                prefix = app.config.needs_part_prefix
                temp_need['title'] = temp_need['content']

            for col in current_needtable["columns"]:
                if col == "ID":
                    row += row_col_maker(app, fromdocname, env.needs_all_needs, temp_need, "id", make_ref=True,
                                         prefix=prefix)
                elif col == "TITLE":
                    row += row_col_maker(
                        app, fromdocname, env.needs_all_needs, temp_need, "title",
                        prefix=app.config.needs_part_prefix)
                elif col == "INCOMING":
                    row += row_col_maker(app, fromdocname, env.needs_all_needs, temp_need,
                                         "links_back", ref_lookup=True)
                elif col == "OUTGOING":
                    row += row_col_maker(app, fromdocname, env.needs_all_needs, temp_need, "links", ref_lookup=True)
                else:
                    row += row_col_maker(app, fromdocname, env.needs_all_needs, temp_need, col.lower())
            tbody += row

            # Need part rows
            if current_needtable["show_parts"] and need_info['is_need']:
                for key, part in need_info["parts"].items():
                    row = nodes.row(classes=['need_part'])
                    temp_part = part.copy()  # The dict needs to be manipulated, so that row_col_maker() can be used
                    temp_part['docname'] = need_info['docname']

                    for col in current_needtable["columns"]:
                        if col == "ID":
                            temp_part['id'] = '.'.join([need_info['id'], part['id']])
                            row += row_col_maker(
                                app, fromdocname, env.needs_all_needs, temp_part, "id",
                                make_ref=True, prefix=app.config.needs_part_prefix)
                        elif col == "TITLE":
                            row += row_col_maker(
                                app, fromdocname, env.needs_all_needs, temp_part, "content",
                                prefix=app.config.needs_part_prefix)
                        elif col == "INCOMING":
                            row += row_col_maker(
                                app, fromdocname, env.needs_all_needs, temp_part, "links_back", ref_lookup=True)
                        else:
                            row += row_col_maker(
                                app, fromdocname, env.needs_all_needs, temp_part, col.lower())

                    tbody += row

        if len(found_needs) == 0:
            content.append(no_needs_found_paragraph())

        # add filter information to output
        if current_needtable["show_filters"]:
            content.append(used_filter_paragraph(current_needtable))

        node.replace_self(content)