florath/rmtoo

View on GitHub
rmtoo/outputs/graph.py

Summary

Maintainability
C
7 hrs
Test Coverage
'''
 rmtoo
   Free and Open Source Requirements Management Tool

 Output handler graph.

 (c) 2010-2012,2017 by flonatel GmbH & Co. KG

 For licensing details see COPYING
'''
from __future__ import unicode_literals

from rmtoo.lib.RequirementStatus import RequirementStatusNotDone, \
    RequirementStatusAssigned
from rmtoo.lib.ClassType import ClassTypeImplementable, \
    ClassTypeSelected
from rmtoo.lib.StdOutputParams import StdOutputParams
from rmtoo.lib.Requirement import RequirementType
from rmtoo.lib.ExecutorTopicContinuum import ExecutorTopicContinuum
from rmtoo.lib.logging import tracer
from rmtoo.lib.configuration.Cfg import Cfg
from rmtoo.lib.CreateMakeDependencies import CreateMakeDependencies


class graph(StdOutputParams, ExecutorTopicContinuum, CreateMakeDependencies):
    default_config = Cfg.new_by_json_str(
            """json:{"node_attributes":
               ["Type", "Status", "Class", "Topic", "Priority" ] }""")

    def __init__(self, oconfig):
        '''Create a graph output object.'''
        tracer.info("Called.")
        StdOutputParams.__init__(self, oconfig)
        CreateMakeDependencies.__init__(self)
        self.__used_vcs_id = None
        self.__output_file = None

        if not self._config.is_available('node_attributes'):
            self._config.set_value(
                'node_attributes',
                ["Type", "Status", "Class", "Topic", "Priority", ])

    def topic_continuum_sort(self, vcs_commit_ids, topic_sets):
        '''Because graph2 can only one topic continuum,
           the latest (newest) is used.'''
        self.__used_vcs_id = vcs_commit_ids[-1]
        return [topic_sets[vcs_commit_ids[-1].get_commit()]]

    def topic_set_pre(self, _requirement_set):
        '''This is call in the RequirementSet pre-phase.'''
        tracer.debug("Called")
        # Initialize the graph output
        self.__output_file = open(self._output_filename, "w")
        self.__output_file.write(
                "digraph reqdeps {\nrankdir=BT;\nmclimit=10.0;\n"
                "nslimit=10.0;ranksep=1;\n")

    def requirement_set_sort(self, list_to_sort):
        '''Sort by id.'''
        return sorted(list_to_sort, key=lambda r: r.get_id())

    def topic_set_post(self, _requirement_set):
        '''Write footer - close file.'''
        # Print out a node with the version number:
        self.__output_file.write(
                'ReqVersion [shape=plaintext label="ReqVersion\\n%s"]\n'
                % self.__used_vcs_id)
        self.__output_file.write("}\n")
        self.__output_file.close()

    def requirement(self, requirement):
        '''Output the given requirement.'''
        self.__output_file.write(
            '"%s" [%s];\n' %
            (requirement.get_id(),
             self.node_attributes(requirement, self._config)))

        for d in requirement.incoming:
            self.__output_file.write('"%s" -> "%s";\n' %
                                     (requirement.get_id(), d.get_id()))

# TODO: currently the =default_config is needed for graph2
    @staticmethod
    def node_attributes(req, config=default_config):

        def get_conf_attr(attr):
            return config.is_available("node_attributes") \
                and attr in config.get_value("node_attributes")

        # Colorize the current requirement depending on type
        nodeparam = []
        if get_conf_attr("Type") \
                and req.get_value("Type") \
                == RequirementType.initial_requirement:
            nodeparam.append("color=orange")
        if get_conf_attr("Type") \
                and req.get_value("Type") == RequirementType.design_decision:
            nodeparam.append("color=green")

        if get_conf_attr("Status"):
            req_status = req.get_value("Status")

            if isinstance(req_status, RequirementStatusNotDone):
                nodeparam.append("fontcolor=red")
            elif isinstance(req_status, RequirementStatusAssigned):
                nodeparam.append("fontcolor=blue")

            label = 'label="%s' % req.get_id().replace("/", "\\n/")

            if get_conf_attr("Priority"):
                label += "\\n[%4.2f]" % (req.get_value("Priority") * 10)

            if get_conf_attr("EffortEstimation"):
                est_effort = req.get_value("Effort estimation")
                if est_effort is not None:
                    label += "\\n(%d EfEU)" % est_effort

            label += '"'
            nodeparam.append(label)

        if get_conf_attr("Class"):
            rclass = req.get_value("Class")
            if isinstance(rclass, ClassTypeImplementable):
                nodeparam.append("shape=octagon")
            elif isinstance(rclass, ClassTypeSelected):
                nodeparam.append("shape=box")

        return ",".join(nodeparam)

    def cmad_topic_continuum_pre(self, _):
        '''Write out the one and only dependency to all the requirements.'''
        tracer.debug("Called.")
        CreateMakeDependencies.write_reqs_dep(self._cmad_file,
                                              self._output_filename)