florath/rmtoo

View on GitHub
rmtoo/lib/analytics/TopicCohe.py

Summary

Maintainability
A
50 mins
Test Coverage
'''
 rmtoo
   Free and Open Source Requirements Management Tool

  Coherence of one topic.

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

 For licensing details see COPYING
'''
from six import iteritems

from rmtoo.lib.analytics.Base import Base
from rmtoo.lib.analytics.Result import Result
from rmtoo.lib.logging import tracer


class TopicCohe(Base):
    '''Class for checking topic coherence.'''

    def __init__(self, _):
        '''Sets up the TopicCohe object for use.'''
        Base.__init__(self)
        self.__req2topics = {}
        self.__tcnt = {}

    def __add_req2topic(self, req_id, topic):
        '''Add req_id / topic to cache.'''
        if req_id not in self.__req2topics:
            self.__req2topics[req_id] = []
        self.__req2topics[req_id].append(topic)

    def topic_pre(self, topic):
        '''Collect the relation between requirement and topic.'''
        req_set = topic.get_requirement_set()
        if req_set is None:
            return
        for req_id in req_set.get_all_requirement_ids():
            self.__add_req2topic(req_id, topic)

    def _get_tcnt(self):
        '''Returns the internal state.
           This is only used by the unit tests.'''
        return self.__tcnt

    def _add_topic_relation(self, topic_a, topic_b):
        '''Add the relation between topic_a and topic_b.
           (Here only one _ is used because this is used by the unit tests.)'''
        # If not there, add the initial count [0, 0]
        for topic in [topic_a.name, topic_b.name]:
            if topic not in self.__tcnt:
                self.__tcnt[topic] = [0, 0]

        # Add relation to both directions if the topic is the same or
        # a parent of the topic.

        # Iff self: add a 3!
        if topic_a == topic_b:
            self.__tcnt[topic_a.name][0] += 3
        elif topic_a.is_self_of_ancient(topic_b):
            # 2: because it is one incoming and one outgoing
            self.__tcnt[topic_b.name][0] += 2
        else:
            self.__tcnt[topic_a.name][1] += 1
            self.__tcnt[topic_b.name][1] += 1

    def __eval_link(self, req_a, req_b):
        '''Add all the links between all topics of req_a and req_b.'''
        # If either one of the requirements is not in the topic,
        # skip this step
        if req_a.get_id() not in self.__req2topics \
           or req_b.get_id() not in self.__req2topics:
            tracer.debug("One of the requirements is not in the topic - "
                         "skipping evaluation [%s] [%s]",
                         req_a.get_id(), req_b.get_id())
            return

        for topic_a in self.__req2topics[req_a.get_id()]:
            for topic_b in self.__req2topics[req_b.get_id()]:
                self._add_topic_relation(topic_a, topic_b)

    def topic_set_post(self, topic_set):
        '''This is call in the TopicsSet post-phase.'''
        # pylint: disable=consider-iterating-dictionary
        for req_id in self.__req2topics.keys():
            req_a = topic_set.get_topic_set().get_requirement_set().\
                       get_requirement(req_id)
            for req_b in req_a.incoming:
                self.__eval_link(req_a, req_b)

        for topic, cnt in iteritems(self.__tcnt):
            if cnt[0] <= cnt[1]:
                self.add_result(Result(
                    "TopicCohe", topic,
                    - 10, ["%s: Topic coherence inadequate: "
                           "inner %d / outer %d"
                           % (topic, cnt[0], cnt[1])]))