Source code for utils.compare

# -*- coding: UTF-8 -*-
"""
:filename: sppas.src.utils.compare.py
:author:   Brigitte Bigi
:contact:  develop@sppas.org
:summary:  Utilities to compare data.

.. _This file is part of SPPAS: http://www.sppas.org/
..
    -------------------------------------------------------------------------

     ___   __    __    __    ___
    /     |  \  |  \  |  \  /              the automatic
    \__   |__/  |__/  |___| \__             annotation and
       \  |     |     |   |    \             analysis
    ___/  |     |     |   | ___/              of speech

    Copyright (C) 2011-2021  Brigitte Bigi
    Laboratoire Parole et Langage, Aix-en-Provence, France

    Use of this software is governed by the GNU Public License, version 3.

    SPPAS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SPPAS 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with SPPAS. If not, see <http://www.gnu.org/licenses/>.

    This banner notice must not be removed.

    -------------------------------------------------------------------------

"""

import logging

from sppas.src.config.makeunicode import u
from sppas.src.config.makeunicode import text_type
from sppas.src.config.makeunicode import binary_type
from .datatype import sppasType

# ---------------------------------------------------------------------------


[docs]class sppasCompare(object): """Utility class to compare data. >>> sc = sppasCompare() >>> d1 = {1:"one", 2:"two"} >>> d2 = {2:"TWO", 1:"ONE"} >>> sc.equals(d1, d2) >>> True >>> sc.equals_lists(d1.keys(), d2.keys()) >>> False >>> sc.set_case_sensitive(True) >>> sc.equals(d1, d2) >>> False """
[docs] def __init__(self, verbose=False, case_sensitive=False): """Create a sppasCompare instance and set options. :param verbose: (bool) Print comparison results on stdout :param case_sensitive: (bool) Only to compare strings """ self._verbose = verbose self._case_sensitive = case_sensitive
# -----------------------------------------------------------------------
[docs] def set_verbose(self, v): """Print comparison results on stdout or not. :param v: (bool) Enable or disable verbosity """ self._verbose = bool(v)
# -----------------------------------------------------------------------
[docs] def set_case_sensitive(self, v): """Compare strings with lower/upper case. :param v: (bool) Enable or not the case sensitive comparison of strings """ self._case_sensitive = bool(v)
# -----------------------------------------------------------------------
[docs] def equals(self, data1, data2): """Compare two data sets of any type. :param data1 (any) The data to compare. :param data2 (any) The data to be compared with. :returns: (bool) whether the 2 data sets are equals or not """ if data1 is None or data2 is None: if self._verbose: logging.info("TypeError: None instead of data.") return False if type(data1) is list: return self.equals_lists(data1, data2) if sppasType.is_dict(data1) is True: return self.equals_dictionaries(data1, data2) return self.equals_items(data1, data2)
# -----------------------------------------------------------------------
[docs] def equals_lists(self, list1, list2): """Compare two lists. :param list1 (list) The list to compare. :param list2 (list) The list to be compared with. :returns: (bool) whether the 2 lists are equals or not """ if list1 is None or list2 is None: if self._verbose is True: logging.info("TypeError: None instead of lists.") return False if type(list1) != type(list2) or \ type(list1) is not list or \ type(list2) is not list: if self._verbose is True: logging.info("TypeError: Not same types (expected 2 lists).") return False if len(list1) != len(list2): if self._verbose is True: logging.info("FALSE: Not the same number of items: {0} {1}." "".format(len(list1), len(list2))) return False for item1, item2 in zip(list1, list2): if sppasType.is_dict(item1) is True: items_are_equals = self.equals_dictionaries(item1, item2) elif type(item1) is list: items_are_equals = self.equals_lists(item1, item2) else: items_are_equals = self.equals_items(item1, item2) if items_are_equals is False: return False return True
# -----------------------------------------------------------------------
[docs] def equals_dictionaries(self, dict1, dict2): """Compare two dictionaries. :param dict1: (dict or collection) The dict to compare. :param dict2: (dict or collection) The dict to be compared with. :returns: (bool) whether the 2 dictionaries are equals or not """ if dict1 is None or dict2 is None: if self._verbose is True: logging.info("TypeError: None instead of lists.") return False if sppasType.is_dict(dict1) is False or \ sppasType.is_dict(dict2) is False: if self._verbose is True: logging.info("TypeError: " "Not same types (expected two dictionaries).") return False shared_keys = set(dict2.keys()) & set(dict2.keys()) if not len(shared_keys) == len(dict1.keys()) or \ not len(shared_keys) == len(dict2.keys()): if self._verbose is True: logging.info("FALSE: not shared keys: {0} vs {1}" "".format(dict1.keys(), dict2.keys())) return False for key in dict1: if sppasType.is_dict(dict1[key]) is True: items_are_equals = self.equals_dictionaries(dict1[key], dict2[key]) elif type(dict1[key]) is list: items_are_equals = self.equals_lists(dict1[key], dict2[key]) else: items_are_equals = self.equals_items(dict1[key], dict2[key]) if items_are_equals is False: return False return True
# -----------------------------------------------------------------------
[docs] def equals_items(self, item1, item2): """Compare 2 items of type string or numeric. :param item1: The string or numeric to compare :param item2: The string or numeric to be compared with :returns: (bool) whether the 2 items are equals or not """ if isinstance(item1, (text_type, binary_type)) is True: return self.equals_strings(item1, item2) if type(item1) is float or type(item2) is float: if round(item1, 4) != round(item2, 4): if self._verbose is True: logging.info("Float values rounded to " "4 digits are not equals: " "{:0.4f} != {:0.4f}".format(item1, item2)) return False return True if item1 != item2: if self._verbose is True: logging.info("Not equals: {0} {1}".format(item1, item2)) return False return True
# -----------------------------------------------------------------------
[docs] def equals_strings(self, item1, item2): """Compare 2 data of type string or unicode. :param item1: The string to compare :param item2: The string to be compared with :returns: (bool) whether the 2 items are equals or not """ if isinstance(item1, (text_type, binary_type)) is False or \ isinstance(item2, (text_type, binary_type)) is False: if self._verbose is True: logging.info("TypeError: Not same types " "(expected two strings).") return False if isinstance(item1, binary_type): item1 = u(item1) if isinstance(item2, binary_type): item2 = u(item2) if self._case_sensitive is False: return item1.lower() == item2.lower() return item1 == item2
# ----------------------------------------------------------------------
[docs] @staticmethod def contains(list1, list2): """Check if a list is contained in another one. :param list1: (list) :param list2: (list) :returns: (bool) """ for i in range(len(list1) - len(list2) + 1): if list1[i:i + len(list2)] == list2: return True return False