Esempio n. 1
0
def process_needgantt(app, doctree, fromdocname):
    # Replace all needgantt nodes with a list of the collected needs.
    env = app.builder.env

    link_types = env.config.needs_extra_links
    allowed_link_types_options = [
        link.upper() for link in env.config.needs_flow_link_types
    ]

    # NEEDGANTT
    for node in doctree.traverse(Needgantt):
        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_needgantt = env.need_all_needgantts[id]
        all_needs_dict = env.needs_all_needs

        content = []
        try:
            if "sphinxcontrib.plantuml" not in app.config.extensions:
                raise ImportError
            from sphinxcontrib.plantuml import plantuml
        except ImportError:
            no_plantuml(node)
            continue

        plantuml_block_text = ".. plantuml::\n" \
                              "\n" \
                              "   @startuml" \
                              "   @enduml"
        puml_node = plantuml(plantuml_block_text, **dict())
        puml_node["uml"] = "@startuml\n"
        puml_connections = ""

        # Adding config
        config = current_needgantt['config']
        puml_node["uml"] += add_config(config)

        all_needs = list(all_needs_dict.values())
        found_needs = procces_filters(all_needs, current_needgantt)

        # Scale/timeline handling
        if current_needgantt['timeline'] is not None and current_needgantt[
                'timeline'] != '':
            puml_node["uml"] += 'printscale {}\n'.format(
                current_needgantt["timeline"])

        # Project start date handling
        start_date_string = current_needgantt['start_date']
        start_date_plantuml = None
        if start_date_string is not None and start_date_string != '':
            try:
                start_date = datetime.strptime(start_date_string, '%Y-%m-%d')
                # start_date = datetime.fromisoformat(start_date_string)  # > py3.7 only
            except Exception:
                raise NeedGanttException(
                    'start_date "{}"for needgantt is invalid. '
                    'File: {}:current_needgantt["lineno"]'.format(
                        start_date_string, current_needgantt["docname"]))

            month = MONTH_NAMES[int(start_date.strftime("%-m"))]
            start_date_plantuml = start_date.strftime(
                "%dth of {} %Y".format(month))
        if start_date_plantuml is not None:
            puml_node["uml"] += 'Project starts the {}\n'.format(
                start_date_plantuml)

        # Element handling
        puml_node["uml"] += '\n\' Elements definition \n\n'
        el_link_string = ''
        el_completion_string = ''
        el_color_string = ''
        for need in found_needs:
            complete = None

            if current_needgantt[
                    'milestone_filter'] is None or current_needgantt[
                        'milestone_filter'] == '':
                is_milestone = False
            else:
                is_milestone = filter_single_need(
                    need, current_needgantt['milestone_filter'])
            if current_needgantt['milestone_filter'] is None or current_needgantt['milestone_filter'] == '' or \
                    not is_milestone:
                # Normal gantt element handling
                duration_option = current_needgantt['duration_option']
                duration = need[duration_option]
                complete_option = current_needgantt['completion_option']
                complete = need[complete_option]
                if duration is None or duration == '' or not duration.isdigit(
                ):
                    logger.warning(
                        'Duration not set or invalid for needgantt chart. '
                        'Need: {}. Duration: {}'.format(need["id"], duration))
                    duration = 1
                gantt_element = '[{}] as [{}] lasts {} days\n'.format(
                    need["title"], need["id"], duration)
            else:
                gantt_element = '[{}] as [{}] lasts 0 days\n'.format(
                    need["title"], need["id"])

            el_link_string += '[{}] links to [[{}]]\n'.format(
                need["title"], calculate_link(app, need))

            if complete is not None and complete != '':
                complete = complete.replace('%', '')
                el_completion_string += '[{}] is {}% completed\n'.format(
                    need["title"], complete)

            el_color_string += '[{}] is colored in {}\n'.format(
                need["title"], need["type_color"])

            puml_node["uml"] += gantt_element

        puml_node["uml"] += '\n\' Element links definition \n\n'
        puml_node[
            "uml"] += '\n\' Deactivated, as currently supported by plantuml beta only'
        # ToDo: Activate if linking is working with default plantuml
        # puml_node["uml"] += el_link_string + '\n'

        puml_node["uml"] += '\n\' Element completion definition \n\n'
        puml_node["uml"] += el_completion_string + '\n'

        puml_node["uml"] += '\n\' Element color definition \n\n'
        if current_needgantt['no_color']:
            puml_node["uml"] += '\' Color support deactivated via flag'
        else:
            puml_node["uml"] += el_color_string + '\n'

        # Constrain handling
        puml_node["uml"] += '\n\' Constraints definition \n\n'
        puml_node["uml"] += '\n\' Constraints definition \n\n'
        for need in found_needs:
            if current_needgantt[
                    'milestone_filter'] is None or current_needgantt[
                        'milestone_filter'] == '':
                is_milestone = False
            else:
                is_milestone = filter_single_need(
                    need, current_needgantt['milestone_filter'])
            constrain_types = [
                'starts_with_links', 'starts_after_links', 'ends_with_links'
            ]
            for con_type in constrain_types:
                if is_milestone:
                    keyword = 'happens'
                elif con_type in ['starts_with_links', 'starts_after_links']:
                    keyword = 'starts'
                else:
                    keyword = 'ends'

                if con_type in ['starts_after_links', 'ends_with_links']:
                    start_end_sync = 'end'
                else:
                    start_end_sync = 'start'

                for link_type in current_needgantt[con_type]:
                    start_with_links = need[link_type]
                    for start_with_link in start_with_links:
                        start_need = all_needs_dict[start_with_link]
                        gantt_constraint = '[{}] {} at [{}]\'s ' \
                                           '{}\n'.format(need["id"], keyword, start_need["id"], start_end_sync)
                        puml_node["uml"] += gantt_constraint

        # Create a legend
        if current_needgantt["show_legend"]:
            puml_node["uml"] += '\n\n\' Legend definition \n\n'

            puml_node["uml"] += "legend\n"
            puml_node["uml"] += "|= Color |= Type |\n"
            for need in app.config.needs_types:
                puml_node[
                    "uml"] += "|<back:{color}> {color} </back>| {name} |\n".format(
                        color=need["color"], name=need["title"])
            puml_node["uml"] += "endlegend\n"

        puml_node["uml"] += "\n@enduml"
        puml_node["incdir"] = os.path.dirname(current_needgantt["docname"])
        puml_node["filename"] = os.path.split(
            current_needgantt["docname"])[1]  # Needed for plantuml >= 0.9

        scale = int(current_needgantt['scale'])
        # if scale != 100:
        puml_node['scale'] = scale

        puml_node = nodes.figure('', puml_node)

        if current_needgantt['align'] is not None and len(
                current_needgantt['align']) != '':
            puml_node['align'] = current_needgantt['align']
        else:
            puml_node['align'] = 'center'

        if current_needgantt['caption'] is not None and len(
                current_needgantt['caption']) != '':
            # Make the caption to a link to the original file.
            try:
                if "SVG" in app.config.plantuml_output_format.upper():
                    file_ext = 'svg'
                else:
                    file_ext = 'png'
            except Exception:
                file_ext = 'png'

            gen_flow_link = generate_name(app, puml_node.children[0], file_ext)
            current_file_parts = fromdocname.split('/')
            subfolder_amount = len(current_file_parts) - 1
            img_locaton = '../' * subfolder_amount + '_images/' + gen_flow_link[
                0].split('/')[-1]
            flow_ref = nodes.reference('t',
                                       current_needgantt['caption'],
                                       refuri=img_locaton)
            puml_node += nodes.caption('', '', flow_ref)

        content.append(puml_node)

        if len(content) == 0:
            nothing_found = "No needs passed the filters"
            para = nodes.paragraph()
            nothing_found_node = nodes.Text(nothing_found, nothing_found)
            para += nothing_found_node
            content.append(para)
        if current_needgantt["show_filters"]:
            content.append(get_filter_para(current_needgantt))

        if current_needgantt['debug']:
            content += get_debug_containter(puml_node)

        puml_node['class'] = ['needgantt']
        node.replace_self(content)
Esempio n. 2
0
def process_needgantt(app, doctree, fromdocname):
    # Replace all needgantt nodes with a list of the collected needs.
    env = app.builder.env

    # link_types = env.config.needs_extra_links
    # allowed_link_types_options = [link.upper() for link in env.config.needs_flow_link_types]

    # NEEDGANTT
    for node in doctree.traverse(Needgantt):
        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_needgantt = env.need_all_needgantts[id]
        all_needs_dict = env.needs_all_needs

        content = []
        try:
            if "sphinxcontrib.plantuml" not in app.config.extensions:
                raise ImportError
            from sphinxcontrib.plantuml import plantuml
        except ImportError:
            no_plantuml(node)
            continue

        plantuml_block_text = ".. plantuml::\n" "\n" "   @startuml" "   @enduml"
        puml_node = plantuml(plantuml_block_text)
        puml_node["uml"] = "@startuml\n"

        # Adding config
        config = current_needgantt["config"]
        puml_node["uml"] += add_config(config)

        all_needs = list(all_needs_dict.values())
        found_needs = process_filters(app, all_needs, current_needgantt)

        # Scale/timeline handling
        if current_needgantt["timeline"]:
            puml_node["uml"] += "printscale {}\n".format(
                current_needgantt["timeline"])

        # Project start date handling
        start_date_string = current_needgantt["start_date"]
        start_date_plantuml = None
        if start_date_string:
            try:
                start_date = datetime.strptime(start_date_string, "%Y-%m-%d")
                # start_date = datetime.fromisoformat(start_date_string)  # > py3.7 only
            except Exception:
                raise NeedGanttException(
                    'start_date "{}"for needgantt is invalid. '
                    'File: {}:current_needgantt["lineno"]'.format(
                        start_date_string, current_needgantt["docname"]))

            month = MONTH_NAMES[int(start_date.strftime("%m"))]
            start_date_plantuml = start_date.strftime(f"%dth of {month} %Y")
        if start_date_plantuml:
            puml_node["uml"] += f"Project starts the {start_date_plantuml}\n"

        # Element handling
        puml_node["uml"] += "\n' Elements definition \n\n"
        el_link_string = ""
        el_completion_string = ""
        el_color_string = ""
        for need in found_needs:
            complete = None

            if current_needgantt["milestone_filter"]:
                is_milestone = filter_single_need(
                    app, need, current_needgantt["milestone_filter"])
            else:
                is_milestone = False

            if current_needgantt["milestone_filter"] and is_milestone:
                gantt_element = "[{}] as [{}] lasts 0 days\n".format(
                    need["title"], need["id"])
            else:  # Normal gantt element handling
                duration_option = current_needgantt["duration_option"]
                duration = need[duration_option]
                complete_option = current_needgantt["completion_option"]
                complete = need[complete_option]
                if not (duration and duration.isdigit()):
                    logger.warning(
                        "Duration not set or invalid for needgantt chart. "
                        "Need: {}. Duration: {}".format(need["id"], duration))
                    duration = 1
                gantt_element = "[{}] as [{}] lasts {} days\n".format(
                    need["title"], need["id"], duration)

            el_link_string += "[{}] links to [[{}]]\n".format(
                need["title"], calculate_link(app, need, fromdocname))

            if complete:
                complete = complete.replace("%", "")
                el_completion_string += "[{}] is {}% completed\n".format(
                    need["title"], complete)

            el_color_string += "[{}] is colored in {}\n".format(
                need["title"], need["type_color"])

            puml_node["uml"] += gantt_element

        puml_node["uml"] += "\n' Element links definition \n\n"
        puml_node[
            "uml"] += "\n' Deactivated, as currently supported by plantuml beta only"
        # ToDo: Activate if linking is working with default plantuml
        # puml_node["uml"] += el_link_string + '\n'

        puml_node["uml"] += "\n' Element completion definition \n\n"
        puml_node["uml"] += el_completion_string + "\n"

        puml_node["uml"] += "\n' Element color definition \n\n"
        if current_needgantt["no_color"]:
            puml_node["uml"] += "' Color support deactivated via flag"
        else:
            puml_node["uml"] += el_color_string + "\n"

        # Constrain handling
        puml_node["uml"] += "\n' Constraints definition \n\n"
        puml_node["uml"] += "\n' Constraints definition \n\n"
        for need in found_needs:
            if current_needgantt["milestone_filter"]:
                is_milestone = filter_single_need(
                    app, need, current_needgantt["milestone_filter"])
            else:
                is_milestone = False
            constrain_types = [
                "starts_with_links", "starts_after_links", "ends_with_links"
            ]
            for con_type in constrain_types:
                if is_milestone:
                    keyword = "happens"
                elif con_type in ["starts_with_links", "starts_after_links"]:
                    keyword = "starts"
                else:
                    keyword = "ends"

                if con_type in ["starts_after_links", "ends_with_links"]:
                    start_end_sync = "end"
                else:
                    start_end_sync = "start"

                for link_type in current_needgantt[con_type]:
                    start_with_links = need[link_type]
                    for start_with_link in start_with_links:
                        start_need = all_needs_dict[start_with_link]
                        gantt_constraint = "[{}] {} at [{}]'s " "{}\n".format(
                            need["id"], keyword, start_need["id"],
                            start_end_sync)
                        puml_node["uml"] += gantt_constraint

        # Create a legend
        if current_needgantt["show_legend"]:
            puml_node["uml"] += create_legend(app.config.needs_types)

        puml_node["uml"] += "\n@enduml"
        puml_node["incdir"] = os.path.dirname(current_needgantt["docname"])
        puml_node["filename"] = os.path.split(
            current_needgantt["docname"])[1]  # Needed for plantuml >= 0.9

        scale = int(current_needgantt["scale"])
        # if scale != 100:
        puml_node["scale"] = scale

        puml_node = nodes.figure("", puml_node)

        puml_node["align"] = current_needgantt["align"] or "center"

        if current_needgantt["caption"]:
            # Make the caption to a link to the original file.
            try:
                if "SVG" in app.config.plantuml_output_format.upper():
                    file_ext = "svg"
                else:
                    file_ext = "png"
            except Exception:
                file_ext = "png"

            gen_flow_link = generate_name(app, puml_node.children[0], file_ext)
            current_file_parts = fromdocname.split("/")
            subfolder_amount = len(current_file_parts) - 1
            img_location = "../" * subfolder_amount + "_images/" + gen_flow_link[
                0].split("/")[-1]
            flow_ref = nodes.reference("t",
                                       current_needgantt["caption"],
                                       refuri=img_location)
            puml_node += nodes.caption("", "", flow_ref)

        # Add lineno to node
        puml_node.line = current_needgantt["lineno"]

        content.append(puml_node)

        if len(content) == 0:
            nothing_found = "No needs passed the filters"
            para = nodes.paragraph()
            nothing_found_node = nodes.Text(nothing_found, nothing_found)
            para += nothing_found_node
            content.append(para)
        if current_needgantt["show_filters"]:
            content.append(get_filter_para(current_needgantt))

        if current_needgantt["debug"]:
            content += get_debug_container(puml_node)

        puml_node["class"] = ["needgantt"]
        node.replace_self(content)
def process_needsequence(app, doctree, fromdocname):
    # Replace all needsequence nodes with a list of the collected needs.
    env = app.builder.env

    link_types = env.config.needs_extra_links
    allowed_link_types_options = [
        link.upper() for link in env.config.needs_flow_link_types
    ]

    # NEEDSEQUENCE
    for node in doctree.traverse(Needsequence):
        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_needsequence = env.need_all_needsequences[id]
        all_needs_dict = env.needs_all_needs

        option_link_types = [
            link.upper() for link in current_needsequence['link_types']
        ]
        for lt in option_link_types:
            if lt not in [link['option'].upper() for link in link_types]:
                logger.warning(
                    'Unknown link type {link_type} in needsequence {flow}. Allowed values:'
                    ' {link_types}'.format(
                        link_type=lt,
                        flow=current_needsequence['target_node'],
                        link_types=",".join(link_types)))

        content = []
        try:
            if "sphinxcontrib.plantuml" not in app.config.extensions:
                raise ImportError
            from sphinxcontrib.plantuml import plantuml
        except ImportError:
            no_plantuml(node)
            continue

        plantuml_block_text = ".. plantuml::\n" \
                              "\n" \
                              "   @startuml" \
                              "   @enduml"
        puml_node = plantuml(plantuml_block_text, **dict())
        puml_node["uml"] = "@startuml\n"

        # Adding config
        config = current_needsequence['config']
        puml_node["uml"] += add_config(config)

        all_needs = list(all_needs_dict.values())

        start_needs_id = [
            x.strip() for x in re.split(";|,", current_needsequence['start'])
        ]
        if len(start_needs_id) == 0:
            raise NeedsequenceDirective('No start-id set for needsequence'
                                        ' File '
                                        ':{}'.format(
                                            {current_needsequence["docname"]},
                                            current_needsequence["lineno"]))

        puml_node["uml"] += '\n\' Nodes definition \n\n'

        # Add  start participants
        p_string = ''
        c_string = ''
        for need_id in start_needs_id:
            try:
                need = all_needs_dict[need_id.strip()]
            except KeyError:
                raise NeedSequenceException(
                    'Given {} in needsequence unknown.'
                    ' File {}'
                    ':{}'.format(need_id, current_needsequence["docname"],
                                 current_needsequence["lineno"]))

            # Add children of participants
            msg_receiver_needs, p_string_new, c_string_new = get_message_needs(
                need,
                current_needsequence['link_types'],
                all_needs_dict,
                filter=current_needsequence['filter'])
            p_string += p_string_new
            c_string += c_string_new

        puml_node["uml"] += p_string

        puml_node["uml"] += '\n\' Connection definition \n\n'
        puml_node["uml"] += c_string

        # Create a legend
        if current_needsequence["show_legend"]:
            puml_node["uml"] += '\n\n\' Legend definition \n\n'

            puml_node["uml"] += "legend\n"
            puml_node["uml"] += "|= Color |= Type |\n"
            for need in app.config.needs_types:
                puml_node[
                    "uml"] += "|<back:{color}> {color} </back>| {name} |\n".format(
                        color=need["color"], name=need["title"])
            puml_node["uml"] += "endlegend\n"

        puml_node["uml"] += "\n@enduml"
        puml_node["incdir"] = os.path.dirname(current_needsequence["docname"])
        puml_node["filename"] = os.path.split(
            current_needsequence["docname"])[1]  # Needed for plantuml >= 0.9

        scale = int(current_needsequence['scale'])
        # if scale != 100:
        puml_node['scale'] = scale

        puml_node = nodes.figure('', puml_node)

        if current_needsequence['align'] is not None and len(
                current_needsequence['align']) != '':
            puml_node['align'] = current_needsequence['align']
        else:
            puml_node['align'] = 'center'

        if current_needsequence['caption'] is not None and len(
                current_needsequence['caption']) != '':
            # Make the caption to a link to the original file.
            try:
                if "SVG" in app.config.plantuml_output_format.upper():
                    file_ext = 'svg'
                else:
                    file_ext = 'png'
            except Exception:
                file_ext = 'png'

            gen_flow_link = generate_name(app, puml_node.children[0], file_ext)
            current_file_parts = fromdocname.split('/')
            subfolder_amount = len(current_file_parts) - 1
            img_locaton = '../' * subfolder_amount + '_images/' + gen_flow_link[
                0].split('/')[-1]
            flow_ref = nodes.reference('t',
                                       current_needsequence['caption'],
                                       refuri=img_locaton)
            puml_node += nodes.caption('', '', flow_ref)

        content.append(puml_node)

        if len(content) == 0:
            nothing_found = "No needs passed the filters"
            para = nodes.paragraph()
            nothing_found_node = nodes.Text(nothing_found, nothing_found)
            para += nothing_found_node
            content.append(para)
        if current_needsequence["show_filters"]:
            content.append(get_filter_para(current_needsequence))

        if current_needsequence['debug']:
            content += get_debug_containter(puml_node)

        node.replace_self(content)
def process_needsequence(app, doctree, fromdocname):
    # Replace all needsequence nodes with a list of the collected needs.
    env = app.builder.env

    link_types = env.config.needs_extra_links
    # allowed_link_types_options = [link.upper() for link in env.config.needs_flow_link_types]

    # NEEDSEQUENCE
    for node in doctree.traverse(Needsequence):
        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_needsequence = env.need_all_needsequences[id]
        all_needs_dict = env.needs_all_needs

        option_link_types = [
            link.upper() for link in current_needsequence["link_types"]
        ]
        for lt in option_link_types:
            if lt not in [link["option"].upper() for link in link_types]:
                logger.warning(
                    "Unknown link type {link_type} in needsequence {flow}. Allowed values:"
                    " {link_types}".format(
                        link_type=lt,
                        flow=current_needsequence["target_node"],
                        link_types=",".join(link_types)))

        content = []
        try:
            if "sphinxcontrib.plantuml" not in app.config.extensions:
                raise ImportError
            from sphinxcontrib.plantuml import plantuml
        except ImportError:
            no_plantuml(node)
            continue

        plantuml_block_text = ".. plantuml::\n" "\n" "   @startuml" "   @enduml"
        puml_node = plantuml(plantuml_block_text)
        puml_node["uml"] = "@startuml\n"

        # Adding config
        config = current_needsequence["config"]
        puml_node["uml"] += add_config(config)

        # all_needs = list(all_needs_dict.values())

        start_needs_id = [
            x.strip() for x in re.split(";|,", current_needsequence["start"])
        ]
        if len(start_needs_id) == 0:
            raise NeedsequenceDirective("No start-id set for needsequence"
                                        " File {}"
                                        ":{}".format(
                                            {current_needsequence["docname"]},
                                            current_needsequence["lineno"]))

        puml_node["uml"] += "\n' Nodes definition \n\n"

        # Add  start participants
        p_string = ""
        c_string = ""
        for need_id in start_needs_id:
            try:
                need = all_needs_dict[need_id.strip()]
            except KeyError:
                raise NeedSequenceException(
                    "Given {} in needsequence unknown."
                    " File {}"
                    ":{}".format(need_id, current_needsequence["docname"],
                                 current_needsequence["lineno"]))

            # Add children of participants
            _msg_receiver_needs, p_string_new, c_string_new = get_message_needs(
                app,
                need,
                current_needsequence["link_types"],
                all_needs_dict,
                filter=current_needsequence["filter"])
            p_string += p_string_new
            c_string += c_string_new

        puml_node["uml"] += p_string

        puml_node["uml"] += "\n' Connection definition \n\n"
        puml_node["uml"] += c_string

        # Create a legend
        if current_needsequence["show_legend"]:
            puml_node["uml"] += create_legend(app.config.needs_types)

        puml_node["uml"] += "\n@enduml"
        puml_node["incdir"] = os.path.dirname(current_needsequence["docname"])
        puml_node["filename"] = os.path.split(
            current_needsequence["docname"])[1]  # Needed for plantuml >= 0.9

        scale = int(current_needsequence["scale"])
        # if scale != 100:
        puml_node["scale"] = scale

        puml_node = nodes.figure("", puml_node)

        if current_needsequence["align"]:
            puml_node["align"] = current_needsequence["align"]
        else:
            puml_node["align"] = "center"

        if current_needsequence["caption"]:
            # Make the caption to a link to the original file.
            try:
                if "SVG" in app.config.plantuml_output_format.upper():
                    file_ext = "svg"
                else:
                    file_ext = "png"
            except Exception:
                file_ext = "png"

            gen_flow_link = generate_name(app, puml_node.children[0], file_ext)
            current_file_parts = fromdocname.split("/")
            subfolder_amount = len(current_file_parts) - 1
            img_locaton = "../" * subfolder_amount + "_images/" + gen_flow_link[
                0].split("/")[-1]
            flow_ref = nodes.reference("t",
                                       current_needsequence["caption"],
                                       refuri=img_locaton)
            puml_node += nodes.caption("", "", flow_ref)

        # Add lineno to node
        puml_node.line = current_needsequence["lineno"]

        content.append(puml_node)

        if len(content) == 0:
            nothing_found = "No needs passed the filters"
            para = nodes.paragraph()
            nothing_found_node = nodes.Text(nothing_found, nothing_found)
            para += nothing_found_node
            content.append(para)
        if current_needsequence["show_filters"]:
            content.append(get_filter_para(current_needsequence))

        if current_needsequence["debug"]:
            content += get_debug_container(puml_node)

        node.replace_self(content)