<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (C) 2009-2014:
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.


import re
import itertools


from shinken.misc.datamanager import DataManager


# Sort hosts and services by impact, states and co
def hst_srv_sort(s1, s2):
    if s1.business_impact > s2.business_impact:
        return -1
    if s2.business_impact > s1.business_impact:
        return 1

    # Ok, we compute a importance value so
    # For host, the order is UP, UNREACH, DOWN
    # For service: OK, UNKNOWN, WARNING, CRIT
    # And DOWN is before CRITICAL (potential more impact)
    tab = {'host': {0: 0, 1: 4, 2: 1},
           'service': {0: 0, 1: 2, 2: 3, 3: 1}
           }
    state1 = tab[s1.__class__.my_type].get(s1.state_id, 0)
    state2 = tab[s2.__class__.my_type].get(s2.state_id, 0)
    # ok, here, same business_impact
    # Compare warn and crit state
    if state1 > state2:
        return -1
    if state2 > state1:
        return 1

    # Ok, so by name...
    if s1.get_full_name() > s2.get_full_name():
        return 1
    else:
        return -1


# Sort hosts and services by impact, states and co
def worse_first(s1, s2):
    # Ok, we compute a importance value so
    # For host, the order is UP, UNREACH, DOWN
    # For service: OK, UNKNOWN, WARNING, CRIT
    # And DOWN is before CRITICAL (potential more impact)
    tab = {'host': {0: 0, 1: 4, 2: 1},
           'service': {0: 0, 1: 2, 2: 3, 3: 1}
           }
    state1 = tab[s1.__class__.my_type].get(s1.state_id, 0)
    state2 = tab[s2.__class__.my_type].get(s2.state_id, 0)

    # ok, here, same business_impact
    # Compare warn and crit state
    if state1 > state2:
        return -1
    if state2 > state1:
        return 1

    # Same? ok by business impact
    if s1.business_impact > s2.business_impact:
        return -1
    if s2.business_impact > s1.business_impact:
        return 1

    # Ok, so by name...
    # Ok, so by name...
    if s1.get_full_name() > s2.get_full_name():
        return -1
    else:
        return 1


# Sort hosts and services by last_state_change time
def last_state_change_earlier(s1, s2):
    # ok, here, same business_impact
    # Compare warn and crit state
    if s1.last_state_change > s2.last_state_change:
        return -1
    if s1.last_state_change < s2.last_state_change:
        return 1

    return 0


class WebUIDataManager(DataManager):

    def __init__(self, rg=None):
        super(WebUIDataManager, self).__init__()
        self.rg = rg

    @staticmethod
    def _is_related_to(item, contact):
        """ Is the item (host, service, group…) related to the contact?

            In other words, can the user see this item in the WebUI?
            .. todo:: should be a method of Item or Contact…
                      but the shinken model is incomplete,
                      so we have to do it here.

            :returns: True or False
        """
        # if the user is an admin, show all
        if contact is None or contact.is_admin:
            return True

        # May be the user is a direct contact
        if hasattr(item, 'contacts') and contact in item.contacts:
            return True
        # TODO: add a notified_contact pass

        # May be it's a contact of a linked items
        # source problems or impacts)
        if hasattr(item, 'source_problems'):
            for s in item.source_problems:
                if contact in s.contacts:
                    return True

        if item.__class__.my_type == 'hostgroup':
            for h in item.get_hosts():
                if contact in h.contacts:
                    return True

        # May be it's a contact of a sub item ...
        if item.__class__.my_type == 'servicegroup':
            for s in item.get_services():
                if contact in s.contacts:
                    return True

        # Now impacts related maybe?
        if hasattr(item, 'impacts'):
            for imp in item.impacts:
                if contact in imp.contacts:
                    return True

        return False

    @staticmethod
    def _only_related_to(lst, contact):
        """ This function is just a wrapper to _is_related_to.

            Kept for backward compatibility reasons.

            :returns: List of elements related to the contact
        """
        return [l for l in lst if WebUIDataManager._is_related_to(l, contact)]

    ##
    # Hosts
    ##
    def get_hosts(self, user=None, get_impacts=True):
        """ Get a list of all hosts.

            :param user: concerned user
            :param get_impacts: should impact hosts be included in the list?
            :returns: list of all hosts
        """
        items = super(WebUIDataManager, self).get_hosts()
        if not get_impacts:
            items = [i for i in items if not i.is_impact]
        return self._only_related_to(items, user)

    def get_host(self, hname):
        """ Get a host by its hostname. """
        hname = hname.decode('utf8', 'ignore')
        return self.rg.hosts.find_by_name(hname)

    def get_percentage_hosts_state(self, user=None, problem=False):
        """ Get percentage of hosts not in (or in) problems.

            :param problem: False to return the % of hosts not in problems,
                            True to return the % of hosts in problems.
                            False by default
        """
        all_hosts = self.get_hosts(user)
        if not all_hosts:
            return 0

        problems = [h for h in all_hosts
                    if h.state not in ['UP', 'PENDING']
                    and not h.is_impact]

        if problem:
            return int((len(problems) * 100) / float(len(all_hosts)))
        else:
            return int(100 - (len(problems) * 100) / float(len(all_hosts)))

    ##
    # Services
    ##
    def get_services(self, user=None, get_impacts=True):
        """ Get a list of all services.

            :param user: concerned user
            :param get_impacts: should impact services be included in the list?
            :returns: list of all services
        """
        items = super(WebUIDataManager, self).get_services()
        if not get_impacts:
            items = [i for i in items if not i.is_impact]
        return self._only_related_to(items, user)

    def get_service(self, hname, sdesc):
        """ Get a service by its hostname and service description. """
        hname = hname.decode('utf8', 'ignore')
        sdesc = sdesc.decode('utf8', 'ignore')
        return self.rg.services.find_srv_by_name_and_hostname(hname, sdesc)

    def get_percentage_service_state(self, user=None, problem=False):
        """ Get percentage of services not in (or in) problems.

            :param problem: False to return the % of services not in problems,
                            True to return the % of services in problems.
                            False by default
        """
        all_services = self.get_services(user)
        if not all_services:
            return 0

        problems = [s for s in all_services
                    if s.state not in ['OK', 'PENDING']
                    and not s.is_impact]

        if problem:
            return int((len(problems) * 100) / float(len(all_services)))
        else:
            return int(100 - (len(problems) * 100) / float(len(all_services)))

    def search_hosts_and_services(self, search="", user=None, get_impacts=True, sorter=None):
        """ Search hosts and services.

            This method is the heart of the datamanager. All other methods should be based on this one.

            :search: Search string. default=""
            :user: concerned user
            :get_impacts: should impacts be included in the list?
            :sorter: function to sort the items. default=None (means no sorting)
            :returns: list of hosts and services
        """
        items = []
        items.extend(self.get_hosts(user, get_impacts))
        items.extend(self.get_services(user, get_impacts))

        search = [s for s in search.split(' ')]

        for s in search:
            s = s.strip()
            if not s:
                continue

            elts = s.split(':', 1)
            t = 'hst_srv'
            if len(elts) > 1:
                t = elts[0]
                s = elts[1]
                
            s = s.lower()
            t = t.lower()

            if t == 'hst_srv':
                pat = re.compile(s, re.IGNORECASE)
                new_items = []
                for i in items:
                    if pat.search(i.get_full_name()):
                        new_items.append(i)
                    else:
                        for j in (i.impacts + i.source_problems):
                            if pat.search(j.get_full_name()):
                                new_items.append(i)

                if not new_items:
                    for i in items:
                        if pat.search(i.output):
                            new_items.append(i)
                        else:
                            for j in (i.impacts + i.source_problems):
                                if pat.search(j.output):
                                    new_items.append(i)

                items = new_items

            if (t == 'hg' or t == 'hgroup') and s != 'all':
                group = self.get_hostgroup(s)
                if not group:
                    return []  # :TODO:maethor:150716: raise an error
                items = [i for i in items if group in i.get_hostgroups()]

            if (t == 'sg' or t == 'sgroup') and s != 'all':
                group = self.get_servicegroup(s)
                if not group:
                    return []  # :TODO:maethor:150716: raise an error
                items = [i for i in items if group in i.get_servicegroups()]

            if (t == 'cg' or t == 'cgroup') and s != 'all':
                group = self.get_contactgroup(s)
                if not group:
                    return []  # :TODO:maethor:150716: raise an error
                contacts = [c for c in self.get_contacts() if c in group.members]
                items = list(set(itertools.chain(*[self._only_related_to(items, c) for c in contacts])))

            if t == 'realm':
                r = self.get_realm(s)
                if not r:
                    return []  # :TODO:maethor:150716: raise an error
                items = [i for i in items if i.get_realm() == r]

            if t == 'htag' and s != 'all':
                items = [i for i in items if s in i.get_host_tags()]

            if t == 'stag' and s != 'all':
                items = [i for i in items if i.__class__.my_type == 'service' and s in i.get_service_tags()]

            if t == 'ctag' and s != 'all':
                contacts = [c for c in self.get_contacts() if s in c.tags]
                items = list(set(itertools.chain(*[self._only_related_to(items, c) for c in contacts])))

            if t == 'type' and s != 'all':
                items = [i for i in items if i.__class__.my_type == s]

            if t == 'bp' or t == 'bi':
                if s.startswith('>='):<commit_msg>Closes #282 - Service object has no attribute get_servicegroups<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (C) 2009-2014:
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#   <NAME>, <EMAIL>
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.


import re
import itertools


from shinken.misc.datamanager import DataManager


# Sort hosts and services by impact, states and co
def hst_srv_sort(s1, s2):
    if s1.business_impact > s2.business_impact:
        return -1
    if s2.business_impact > s1.business_impact:
        return 1

    # Ok, we compute a importance value so
    # For host, the order is UP, UNREACH, DOWN
    # For service: OK, UNKNOWN, WARNING, CRIT
    # And DOWN is before CRITICAL (potential more impact)
    tab = {'host': {0: 0, 1: 4, 2: 1},
           'service': {0: 0, 1: 2, 2: 3, 3: 1}
           }
    state1 = tab[s1.__class__.my_type].get(s1.state_id, 0)
    state2 = tab[s2.__class__.my_type].get(s2.state_id, 0)
    # ok, here, same business_impact
    # Compare warn and crit state
    if state1 > state2:
        return -1
    if state2 > state1:
        return 1

    # Ok, so by name...
    if s1.get_full_name() > s2.get_full_name():
        return 1
    else:
        return -1


# Sort hosts and services by impact, states and co
def worse_first(s1, s2):
    # Ok, we compute a importance value so
    # For host, the order is UP, UNREACH, DOWN
    # For service: OK, UNKNOWN, WARNING, CRIT
    # And DOWN is before CRITICAL (potential more impact)
    tab = {'host': {0: 0, 1: 4, 2: 1},
           'service': {0: 0, 1: 2, 2: 3, 3: 1}
           }
    state1 = tab[s1.__class__.my_type].get(s1.state_id, 0)
    state2 = tab[s2.__class__.my_type].get(s2.state_id, 0)

    # ok, here, same business_impact
    # Compare warn and crit state
    if state1 > state2:
        return -1
    if state2 > state1:
        return 1

    # Same? ok by business impact
    if s1.business_impact > s2.business_impact:
        return -1
    if s2.business_impact > s1.business_impact:
        return 1

    # Ok, so by name...
    # Ok, so by name...
    if s1.get_full_name() > s2.get_full_name():
        return -1
    else:
        return 1


# Sort hosts and services by last_state_change time
def last_state_change_earlier(s1, s2):
    # ok, here, same business_impact
    # Compare warn and crit state
    if s1.last_state_change > s2.last_state_change:
        return -1
    if s1.last_state_change < s2.last_state_change:
        return 1

    return 0


class WebUIDataManager(DataManager):

    def __init__(self, rg=None):
        super(WebUIDataManager, self).__init__()
        self.rg = rg

    @staticmethod
    def _is_related_to(item, contact):
        """ Is the item (host, service, group…) related to the contact?

            In other words, can the user see this item in the WebUI?
            .. todo:: should be a method of Item or Contact…
                      but the shinken model is incomplete,
                      so we have to do it here.

            :returns: True or False
        """
        # if the user is an admin, show all
        if contact is None or contact.is_admin:
            return True

        # May be the user is a direct contact
        if hasattr(item, 'contacts') and contact in item.contacts:
            return True
        # TODO: add a notified_contact pass

        # May be it's a contact of a linked items
        # source problems or impacts)
        if hasattr(item, 'source_problems'):
            for s in item.source_problems:
                if contact in s.contacts:
                    return True

        if item.__class__.my_type == 'hostgroup':
            for h in item.get_hosts():
                if contact in h.contacts:
                    return True

        # May be it's a contact of a sub item ...
        if item.__class__.my_type == 'servicegroup':
            for s in item.get_services():
                if contact in s.contacts:
                    return True

        # Now impacts related maybe?
        if hasattr(item, 'impacts'):
            for imp in item.impacts:
                if contact in imp.contacts:
                    return True

        return False

    @staticmethod
    def _only_related_to(lst, contact):
        """ This function is just a wrapper to _is_related_to.

            Kept for backward compatibility reasons.

            :returns: List of elements related to the contact
        """
        return [l for l in lst if WebUIDataManager._is_related_to(l, contact)]

    ##
    # Hosts
    ##
    def get_hosts(self, user=None, get_impacts=True):
        """ Get a list of all hosts.

            :param user: concerned user
            :param get_impacts: should impact hosts be included in the list?
            :returns: list of all hosts
        """
        items = super(WebUIDataManager, self).get_hosts()
        if not get_impacts:
            items = [i for i in items if not i.is_impact]
        return self._only_related_to(items, user)

    def get_host(self, hname):
        """ Get a host by its hostname. """
        hname = hname.decode('utf8', 'ignore')
        return self.rg.hosts.find_by_name(hname)

    def get_percentage_hosts_state(self, user=None, problem=False):
        """ Get percentage of hosts not in (or in) problems.

            :param problem: False to return the % of hosts not in problems,
                            True to return the % of hosts in problems.
                            False by default
        """
        all_hosts = self.get_hosts(user)
        if not all_hosts:
            return 0

        problems = [h for h in all_hosts
                    if h.state not in ['UP', 'PENDING']
                    and not h.is_impact]

        if problem:
            return int((len(problems) * 100) / float(len(all_hosts)))
        else:
            return int(100 - (len(problems) * 100) / float(len(all_hosts)))

    ##
    # Services
    ##
    def get_services(self, user=None, get_impacts=True):
        """ Get a list of all services.

            :param user: concerned user
            :param get_impacts: should impact services be included in the list?
            :returns: list of all services
        """
        items = super(WebUIDataManager, self).get_services()
        if not get_impacts:
            items = [i for i in items if not i.is_impact]
        return self._only_related_to(items, user)

    def get_service(self, hname, sdesc):
        """ Get a service by its hostname and service description. """
        hname = hname.decode('utf8', 'ignore')
        sdesc = sdesc.decode('utf8', 'ignore')
        return self.rg.services.find_srv_by_name_and_hostname(hname, sdesc)

    def get_percentage_service_state(self, user=None, problem=False):
        """ Get percentage of services not in (or in) problems.

            :param problem: False to return the % of services not in problems,
                            True to return the % of services in problems.
                            False by default
        """
        all_services = self.get_services(user)
        if not all_services:
            return 0

        problems = [s for s in all_services
                    if s.state not in ['OK', 'PENDING']
                    and not s.is_impact]

        if problem:
            return int((len(problems) * 100) / float(len(all_services)))
        else:
            return int(100 - (len(problems) * 100) / float(len(all_services)))

    def search_hosts_and_services(self, search="", user=None, get_impacts=True, sorter=None):
        """ Search hosts and services.

            This method is the heart of the datamanager. All other methods should be based on this one.

            :search: Search string. default=""
            :user: concerned user
            :get_impacts: should impacts be included in the list?
            :sorter: function to sort the items. default=None (means no sorting)
            :returns: list of hosts and services
        """
        items = []
        items.extend(self.get_hosts(user, get_impacts))
        items.extend(self.get_services(user, get_impacts))

        search = [s for s in search.split(' ')]

        for s in search:
            s = s.strip()
            if not s:
                continue

            elts = s.split(':', 1)
            t = 'hst_srv'
            if len(elts) > 1:
                t = elts[0]
                s = elts[1]
                
            s = s.lower()
            t = t.lower()

            if t == 'hst_srv':
                pat = re.compile(s, re.IGNORECASE)
                new_items = []
                for i in items:
                    if pat.search(i.get_full_name()):
                        new_items.append(i)
                    else:
                        for j in (i.impacts + i.source_problems):
                            if pat.search(j.get_full_name()):
                                new_items.append(i)

                if not new_items:
                    for i in items:
                        if pat.search(i.output):
                            new_items.append(i)
                        else:
                            for j in (i.impacts + i.source_problems):
                                if pat.search(j.output):
                                    new_items.append(i)

                items = new_items

            if (t == 'hg' or t == 'hgroup') and s != 'all':
                group = self.get_hostgroup(s)
                if not group:
                    return []  # :TODO:maethor:150716: raise an error
                items = [i for i in items if group in i.get_hostgroups()]

            if (t == 'sg' or t == 'sgroup') and s != 'all':
                group = self.get_servicegroup(s)
                if not group:
                    return []  # :TODO:maethor:150716: raise an error
                items = [i for i in items if group in i.servicegroups]

            if (t == 'cg' or t == 'cgroup') and s != 'all':
                group = self.get_contactgroup(s)
                if not group:
                    return []  # :TODO:maethor:150716: raise an error
                contacts = [c for c in self.get_contacts() if c in group.members]
                items = list(set(itertools.chain(*[self._only_related_to(items, c) for c in contacts])))

            if t == 'realm':
                r = self.get_realm(s)
                if not r:
                    return []  # :TODO:maethor:150716: raise an error
                items = [i for i in items if i.get_realm() == r]

            if t == 'htag' and s != 'all':
                items = [i for i in items if s in i.get_host_tags()]

            if t == 'stag' and s != 'all':
                items = [i for i in items if i.__class__.my_type == 'service' and s in i.get_service_tags()]

            if t == 'ctag' and s != 'all':
                contacts = [c for c in self.get_contacts() if s in c.tags]
                items = list(set(itertools.chain(*[self._only_related_to(items, c) for c in contacts])))

            if t == 'type' and s != 'all':
                items = [i for i in items if i.__class__.my_type == s]

            if t == 'bp' or t == 'bi':
                if s.startswith('>='):
{'fraction_non_alphanumeric': 0.09602148732583515, 'fraction_numerical': 0.012716132281349673, 'mean_word_length': 1.7276785714285714, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5071664', 'n_tokens_mistral': 7366, 'n_tokens_neox': 6894, 'n_words': 2406}