diff --git a/ovos_utils/colors.py b/ovos_utils/colors.py deleted file mode 100644 index a6a90d6b..00000000 --- a/ovos_utils/colors.py +++ /dev/null @@ -1,258 +0,0 @@ -from colorsys import rgb_to_yiq, rgb_to_hls, yiq_to_rgb, hls_to_rgb, rgb_to_hsv, hsv_to_rgb -from colour import Color as _Color -from ovos_utils import camel_case_split - - -class UnrecognizedColorName(ValueError): - """ No color defined with this name """ - - -class Color(_Color): - """ A well defined Color, just the way computers love colors""" - @property - def name(self): - if self.web != self.hex: - return camel_case_split(self.web).lower() - return None - - @staticmethod - def from_name(name): - try: - name = name.lower().replace(" ", "").strip() - return Color(name) - except: - # try to parse color description - color = ColorOutOfSpace() - - if "bright" in name: - color.set_luminance(0.7) - if "dark" in name: - color.set_luminance(0.3) - - if "light" in name: - color.set_saturation(0.4) - if "grey" in name or "gray" in name: - color.set_saturation(0.25) - - if "black" in name: - color.set_luminance(0.1) - if "white" in name: - color.set_luminance(1) - - red = 0.0 - orange = 0.10 - yellow = 0.16 - green = 0.33 - cyan = 0.5 - blue = 0.66 - violet = 0.83 - - if "orange" in name: - color.hue = orange - elif "yellow" in name: - color.hue = yellow - elif "green" in name: - color.hue = green - elif "cyan" in name: - color.hue = cyan - elif "blue" in name: - color.hue = blue - elif "violet" in name: - color.hue = violet - else: - color.hue = red - - return color - - @property - def main_color(self): - """ - reduce to 1 color of the following: - - grey - - black - - white - - orange - - yellow - - green - - cyan - - blue - - violet - - red - """ - if self.saturation <= 0.3: - return Color("grey") - if self.luminance <= 0.15: - return Color("black") - elif self.luminance >= 0.85: - return Color("white") - - thresh = 0.5 - orange = 0.10 - yellow = 0.16 - green = 0.33 - cyan = 0.5 - blue = 0.66 - violet = 0.83 - - if orange - thresh <= self.hue <= orange + thresh: - return Color("orange") - elif yellow - thresh <= self.hue <= yellow + thresh: - return Color("yellow") - elif green - thresh <= self.hue <= green + thresh: - return Color("green") - elif cyan - thresh <= self.hue <= cyan + thresh: - return Color("cyan") - elif blue - thresh <= self.hue <= blue + thresh: - return Color("blue") - elif violet - thresh <= self.hue <= violet + thresh: - return Color("violet") - else: - return Color("red") - - @property - def color_description(self): - if self.web != self.hex: - return camel_case_split(self.web).lower() - name = "" - # light vs dark - if self.luminance <= 0.3: - name += "dark " - elif self.luminance >= 0.6: - name += "bright " - - # hue - # R >== B >= G Red - if self.red >= self.blue >= self.green: - name += "red-ish " - # R >== G >= = B Orange - elif self.red >= self.green >= self.blue: - name += "orange-ish " - # G >= R >== B Yellow - elif self.green >= self.red >= self.blue: - name += "yellow-ish " - # G >== B >= R Green - elif self.green >= self.blue >= self.red: - name += "green-ish " - # B >= G >= R Blue - elif self.blue >= self.green >= self.red: - name += "blue-ish " - # B >= R >== G Violet - elif self.blue >= self.red >= self.green: - name += "purple-ish " - - # luminance - if self.luminance <= 0.15: - name += "black-ish " - elif self.luminance >= 0.85: - name += "white-ish " - - # saturation - if self.saturation >= 0.85: - name += "intense " - name += self.main_color.name + " color" - - return name - - #### HEX #### - @staticmethod - def from_hex(hex_value): - return Color(hex_value) - - #### RGB #### - @property - def rgb255(self): - return (int(self.red * 255), - int(self.green * 255), - int(self.blue * 255)) - - def rgb_percent(self): - return self.rgb - - @staticmethod - def from_rgb(r, g, b): - return Color(rgb=(r / 255, g / 255, b / 255)) - - @staticmethod - def from_rgb_percent(r, g, b): - if isinstance(r, str) or isinstance(g, str) or isinstance(b, str): - r = float(r.replace("%", "")) - g = float(g.replace("%", "")) - b = float(b.replace("%", "")) - return Color(rgb=(r, g, b)) - - #### HSV #### - @staticmethod - def from_hsv(h, s, v): - r, g, b = hsv_to_rgb(h, s, v) - return Color.from_rgb(r, g, b) - - @property - def hsv(self): - return rgb_to_hsv(self.red, self.green, self.blue) - - #### HLS #### - @staticmethod - def from_hls(h, l, s): - r, g, b = hls_to_rgb(h, l, s) - return Color.from_rgb(r, g, b) - - @property - def hls(self): - return rgb_to_hls(self.red, self.green, self.blue) - - #### YIQ #### - @staticmethod - def from_yiq(y, i, q): - r, g, b = yiq_to_rgb(y, i, q) - return Color.from_rgb(r, g, b) - - @property - def yiq(self): - return rgb_to_yiq(self.red, self.green, self.blue) - - def __str__(self): - return self.hex_l - - -class ColorOutOfSpace(Color): - """ Some Human described this color, but humans suck at this""" - @property - def name(self): - # H.P. Lovecraft - https://www.youtube.com/watch?v=4liRxrDzS5I - # return "The Color Out of Space" - return self.hex - - -if __name__ == "__main__": - black = Color() - assert black == Color("black") - - # NOTE this is the web name - try: - color = Color("dark green") - except ValueError: - color = Color("DarkGreen") - color.from_name("dark green") # use this one instead - - assert color.web == "DarkGreen" - assert color.name == "dark green" - - white = Color.from_name("white") - assert white == Color.from_rgb(255, 255, 255) - - color = Color.from_name("NNNNNNNNNNNNNNNNNN") - assert isinstance(color, ColorOutOfSpace) - assert color.name != "black" - assert color.color_description == "black" - - color = Color.from_rgb(0, 120, 240) - assert color.name is None - color.set_saturation(0.3) - color.set_luminance(0.7) - assert color.color_description == "bright blue-ish gray color" - assert color.rgb255 == (156, 179, 202) - - aprox_color = Color.from_name("bright blue-ish grey color") - assert aprox_color.color_description == "bright blue-ish gray color" - assert aprox_color.rgb255 != color.rgb255 - assert aprox_color.rgb255 == (160, 161, 198) diff --git a/ovos_utils/enclosure/detection.py b/ovos_utils/enclosure/detection.py deleted file mode 100644 index b38ebf36..00000000 --- a/ovos_utils/enclosure/detection.py +++ /dev/null @@ -1,49 +0,0 @@ -import subprocess - - -def is_mycroft_sj201(): - cmd = 'i2cdetect -y -a 1 0x04 0x04 | egrep "(04|UU)" | awk \'{print $2}\'' - out = subprocess.check_output(cmd, shell=True).strip() - if out == b"04" or out == b"UU": - return True - return False - - -def is_respeaker_2mic(): - cmd = 'i2cdetect -y -a 1 0x1a 0x1a | egrep "(1a|UU)" | awk \'{print $2}\'' - out = subprocess.check_output(cmd, shell=True).strip() - if out == b"1a" or out == b"UU": - return True - return False - - -def is_respeaker_4mic(): - cmd = 'i2cdetect -y -a 0x35 0x35 | egrep "(35|UU)" | awk \'{print $2}\'' - out = subprocess.check_output(cmd, shell=True).strip() - if out == b"35" or out == b"UU": - return True - return False - - -def is_respeaker_6mic(): - cmd = 'i2cdetect -y -a 0x3b 0x3b | egrep "(3b|UU)" | awk \'{print $2}\'' - out = subprocess.check_output(cmd, shell=True).strip() - if out == b"3b" or out == b"UU": - return True - return False - - -def is_adafruit(): - cmd = 'i2cdetect -y -a 0x4b 0x4b | egrep "(4b|UU)" | awk \'{print $2}\'' - out = subprocess.check_output(cmd, shell=True).strip() - if out == b"4b" or out == b"UU": - return True - return False - - -def is_texas_tas5806(): - cmd = 'i2cdetect -y -a 0x2f 0x2f | egrep "(2f|UU)" | awk \'{print $2}\'' - out = subprocess.check_output(cmd, shell=True).strip() - if out == b"2f" or out == b"UU": - return True - return False diff --git a/ovos_utils/enclosure/template.py b/ovos_utils/enclosure/template.py deleted file mode 100644 index 8a2d88c6..00000000 --- a/ovos_utils/enclosure/template.py +++ /dev/null @@ -1,412 +0,0 @@ -import time -from ovos_utils.log import LOG -from ovos_utils.messagebus import get_mycroft_bus - - -class EnclosureTemplate: - """ - This base class is intended to be used to interface with the hardware - that is running Mycroft. It exposes all possible commands which - can be sent to a Mycroft enclosure implementation. - """ - - def __init__(self, bus=None, name=""): - self._mouth_events = False - self._running = False - self.bus = bus or get_mycroft_bus() - self.log = LOG - self.name = name - - self.bus.on("enclosure.reset", self.on_reset) - - # enclosure commands for Mycroft's Hardware. - self.bus.on("enclosure.system.reset", self.on_system_reset) - self.bus.on("enclosure.system.mute", self.on_system_mute) - self.bus.on("enclosure.system.unmute", self.on_system_unmute) - self.bus.on("enclosure.system.blink", self.on_system_blink) - - # enclosure commands for eyes - self.bus.on('enclosure.eyes.on', self.on_eyes_on) - self.bus.on('enclosure.eyes.off', self.on_eyes_off) - self.bus.on('enclosure.eyes.blink', self.on_eyes_blink) - self.bus.on('enclosure.eyes.narrow', self.on_eyes_narrow) - self.bus.on('enclosure.eyes.look', self.on_eyes_look) - self.bus.on('enclosure.eyes.color', self.on_eyes_color) - self.bus.on('enclosure.eyes.level', self.on_eyes_brightness) - self.bus.on('enclosure.eyes.volume', self.on_eyes_volume) - self.bus.on('enclosure.eyes.spin', self.on_eyes_spin) - self.bus.on('enclosure.eyes.timedspin', self.on_eyes_timed_spin) - self.bus.on('enclosure.eyes.reset', self.on_eyes_reset) - self.bus.on('enclosure.eyes.setpixel', self.on_eyes_set_pixel) - self.bus.on('enclosure.eyes.fill', self.on_eyes_fill) - - # enclosure commands for mouth - self.bus.on("enclosure.mouth.events.activate", - self._activate_mouth_events) - self.bus.on("enclosure.mouth.events.deactivate", - self._deactivate_mouth_events) - self.bus.on("enclosure.mouth.talk", self._on_mouth_talk) - self.bus.on("enclosure.mouth.think", self._on_mouth_think) - self.bus.on("enclosure.mouth.listen", self._on_mouth_listen) - self.bus.on("enclosure.mouth.smile", self._on_mouth_smile) - self.bus.on("enclosure.mouth.viseme", self._on_mouth_viseme) - # mouth/matrix display - self.bus.on("enclosure.mouth.reset", self.on_display_reset) - self.bus.on("enclosure.mouth.text", self.on_text) - self.bus.on("enclosure.mouth.display", self.on_display) - self.bus.on("enclosure.weather.display", self.on_weather_display) - - # audio events - self.bus.on('recognizer_loop:record_begin', self.on_record_begin) - self.bus.on('recognizer_loop:record_end', self.on_record_end) - self.bus.on("recognizer_loop:sleep", self.on_sleep) - self.bus.on('recognizer_loop:audio_output_start', - self.on_audio_output_start) - self.bus.on('recognizer_loop:audio_output_end', - self.on_audio_output_end) - - # other events - self.bus.on("mycroft.awoken", self.on_awake) - self.bus.on("speak", self.on_speak) - self.bus.on("enclosure.notify.no_internet", self.on_no_internet) - - self._activate_mouth_events() - - def shutdown(self): - """ - - """ - self.bus.remove("enclosure.reset", self.on_reset) - self.bus.remove("enclosure.system.reset", self.on_system_reset) - self.bus.remove("enclosure.system.mute", self.on_system_mute) - self.bus.remove("enclosure.system.unmute", self.on_system_unmute) - self.bus.remove("enclosure.system.blink", self.on_system_blink) - - self.bus.remove("enclosure.eyes.on", self.on_eyes_on) - self.bus.remove("enclosure.eyes.off", self.on_eyes_off) - self.bus.remove("enclosure.eyes.blink", self.on_eyes_blink) - self.bus.remove("enclosure.eyes.narrow", self.on_eyes_narrow) - self.bus.remove("enclosure.eyes.look", self.on_eyes_look) - self.bus.remove("enclosure.eyes.color", self.on_eyes_color) - self.bus.remove("enclosure.eyes.brightness", self.on_eyes_brightness) - self.bus.remove("enclosure.eyes.reset", self.on_eyes_reset) - self.bus.remove("enclosure.eyes.timedspin", self.on_eyes_timed_spin) - self.bus.remove("enclosure.eyes.volume", self.on_eyes_volume) - self.bus.remove("enclosure.eyes.spin", self.on_eyes_spin) - self.bus.remove("enclosure.eyes.set_pixel", self.on_eyes_set_pixel) - - self.bus.remove("enclosure.mouth.reset", self.on_display_reset) - self.bus.remove("enclosure.mouth.talk", self.on_talk) - self.bus.remove("enclosure.mouth.think", self.on_think) - self.bus.remove("enclosure.mouth.listen", self.on_listen) - self.bus.remove("enclosure.mouth.smile", self.on_smile) - self.bus.remove("enclosure.mouth.viseme", self.on_viseme) - self.bus.remove("enclosure.mouth.text", self.on_text) - self.bus.remove("enclosure.mouth.display", self.on_display) - self.bus.remove("enclosure.mouth.events.activate", - self._activate_mouth_events) - self.bus.remove("enclosure.mouth.events.deactivate", - self._deactivate_mouth_events) - - self.bus.remove("enclosure.weather.display", self.on_weather_display) - - self.bus.remove("mycroft.awoken", self.on_awake) - self.bus.remove("recognizer_loop:sleep", self.on_sleep) - self.bus.remove("speak", self.on_speak) - self.bus.remove('recognizer_loop:record_begin', self.on_record_begin) - self.bus.remove('recognizer_loop:record_end', self.on_record_end) - self.bus.remove('recognizer_loop:audio_output_start', - self.on_audio_output_start) - self.bus.remove("enclosure.notify.no_internet", self.on_no_internet) - - self._deactivate_mouth_events() - self._running = False - - def run(self): - ''' start enclosure ''' - self._running = True - while self._running: - time.sleep(1) - - # Audio Events - def on_record_begin(self, message=None): - ''' listening started ''' - pass - - def on_record_end(self, message=None): - ''' listening ended ''' - pass - - def on_audio_output_start(self, message=None): - ''' speaking started ''' - pass - - def on_audio_output_end(self, message=None): - ''' speaking started ''' - pass - - def on_awake(self, message=None): - ''' on wakeup animation ''' - pass - - def on_sleep(self, message=None): - ''' on naptime animation ''' - # TODO naptime skill animation should be ond here - pass - - def on_speak(self, message=None): - ''' on speak messages, intended for enclosures that disregard - visemes ''' - pass - - def on_reset(self, message=None): - """The enclosure should restore itself to a started state. - Typically this would be represented by the eyes being 'open' - and the mouth reset to its default (smile or blank). - """ - pass - - # System Events - def on_no_internet(self, message=None): - """ - - Args: - message: - """ - pass - - def on_system_reset(self, message=None): - """The enclosure hardware should reset any CPUs, etc.""" - pass - - def on_system_mute(self, message=None): - """Mute (turn off) the system speaker.""" - pass - - def on_system_unmute(self, message=None): - """Unmute (turn on) the system speaker.""" - pass - - def on_system_blink(self, message=None): - """The 'eyes' should blink the given number of times. - Args: - times (int): number of times to blink - """ - pass - - # Eyes events - def on_eyes_on(self, message=None): - """Illuminate or show the eyes.""" - pass - - def on_eyes_off(self, message=None): - """Turn off or hide the eyes.""" - pass - - def on_eyes_fill(self, message=None): - pass - - def on_eyes_blink(self, message=None): - """Make the eyes blink - Args: - side (str): 'r', 'l', or 'b' for 'right', 'left' or 'both' - """ - pass - - def on_eyes_narrow(self, message=None): - """Make the eyes look narrow, like a squint""" - pass - - def on_eyes_look(self, message=None): - """Make the eyes look to the given side - Args: - side (str): 'r' for right - 'l' for left - 'u' for up - 'd' for down - 'c' for crossed - """ - pass - - def on_eyes_color(self, message=None): - """Change the eye color to the given RGB color - Args: - r (int): 0-255, red value - g (int): 0-255, green value - b (int): 0-255, blue value - """ - pass - - def on_eyes_brightness(self, message=None): - """Set the brightness of the eyes in the display. - Args: - level (int): 1-30, bigger numbers being brighter - """ - pass - - def on_eyes_reset(self, message=None): - """Restore the eyes to their default (ready) state.""" - pass - - def on_eyes_timed_spin(self, message=None): - """Make the eyes 'roll' for the given time. - Args: - length (int): duration in milliseconds of roll, None = forever - """ - pass - - def on_eyes_volume(self, message=None): - """Indicate the volume using the eyes - Args: - volume (int): 0 to 11 - """ - pass - - def on_eyes_spin(self, message=None): - """ - Args: - """ - pass - - def on_eyes_set_pixel(self, message=None): - """ - Args: - """ - pass - - # Mouth events - def _on_mouth_reset(self, message=None): - """Restore the mouth display to normal (blank)""" - if self.mouth_events_active: - self.on_display_reset(message) - - def _on_mouth_talk(self, message=None): - """Show a generic 'talking' animation for non-synched speech""" - if self.mouth_events_active: - self.on_talk(message) - - def _on_mouth_think(self, message=None): - """Show a 'thinking' image or animation""" - if self.mouth_events_active: - self.on_think(message) - - def _on_mouth_listen(self, message=None): - """Show a 'thinking' image or animation""" - if self.mouth_events_active: - self.on_listen(message) - - def _on_mouth_smile(self, message=None): - """Show a 'smile' image or animation""" - if self.mouth_events_active: - self.on_smile(message) - - def _on_mouth_viseme(self, message=None): - """Display a viseme mouth shape for synched speech - Args: - code (int): 0 = shape for sounds like 'y' or 'aa' - 1 = shape for sounds like 'aw' - 2 = shape for sounds like 'uh' or 'r' - 3 = shape for sounds like 'th' or 'sh' - 4 = neutral shape for no sound - 5 = shape for sounds like 'f' or 'v' - 6 = shape for sounds like 'oy' or 'ao' - """ - if self.mouth_events_active: - self.on_viseme(message) - - def _on_mouth_text(self, message=None): - """Display text (scrolling as needed) - Args: - text (str): text string to display - """ - if self.mouth_events_active: - self.on_text(message) - - def _on_mouth_display(self, message=None): - if self.mouth_events_active: - self.on_display(message) - - # Display (faceplate) events - def on_display_reset(self, message=None): - """Restore the mouth display to normal (blank)""" - pass - - def on_talk(self, message=None): - """Show a generic 'talking' animation for non-synched speech""" - pass - - def on_think(self, message=None): - """Show a 'thinking' image or animation""" - pass - - def on_listen(self, message=None): - """Show a 'thinking' image or animation""" - pass - - def on_smile(self, message=None): - """Show a 'smile' image or animation""" - pass - - def on_viseme(self, message=None): - """Display a viseme mouth shape for synched speech - Args: - code (int): 0 = shape for sounds like 'y' or 'aa' - 1 = shape for sounds like 'aw' - 2 = shape for sounds like 'uh' or 'r' - 3 = shape for sounds like 'th' or 'sh' - 4 = neutral shape for no sound - 5 = shape for sounds like 'f' or 'v' - 6 = shape for sounds like 'oy' or 'ao' - """ - pass - - def on_text(self, message=None): - """Display text (scrolling as needed) - Args: - text (str): text string to display - """ - pass - - def on_display(self, message=None): - """Display images on faceplate. Currently supports images up to 16x8, - or half the face. You can use the 'x' parameter to cover the other - half of the faceplate. - Args: - img_code (str): text string that encodes a black and white image - x (int): x offset for image - y (int): y offset for image - refresh (bool): specify whether to clear the faceplate before - displaying the new image or not. - Useful if you'd like to display muliple images - on the faceplate at once. - """ - pass - - def on_weather_display(self, message=None): - """Show a the temperature and a weather icon - - Args: - img_code (char): one of the following icon codes - 0 = sunny - 1 = partly cloudy - 2 = cloudy - 3 = light rain - 4 = raining - 5 = stormy - 6 = snowing - 7 = wind/mist - temp (int): the temperature (either C or F, not indicated) - """ - pass - - @property - def mouth_events_active(self): - return self._mouth_events - - def _activate_mouth_events(self, message=None): - """Enable movement of the mouth with speech""" - self._mouth_events = True - - def _deactivate_mouth_events(self, message=None): - """Disable movement of the mouth with speech""" - self._mouth_events = False diff --git a/ovos_utils/lang/__init__.py b/ovos_utils/lang/__init__.py index b43ccae4..38a87162 100644 --- a/ovos_utils/lang/__init__.py +++ b/ovos_utils/lang/__init__.py @@ -1,23 +1,7 @@ -from os.path import expanduser, isdir, dirname, join -from os import system, makedirs, listdir -from ovos_utils.lang.detect import detect_lang -from ovos_utils.lang.translate import translate_text -from ovos_utils.file_utils import resolve_resource_file - +from os import listdir +from os.path import isdir, join -def get_tts(sentence, lang="en-us", mp3_file="/tmp/google_tx_tts.mp3"): - # TODO privacy issues - https://github.com/OpenVoiceOS/ovos_utils/issues/2 - ext = "mp3" - if not mp3_file.endswith(ext): - mp3_file += "." + ext - mp3_file = expanduser(mp3_file) - if not isdir(dirname(mp3_file)): - makedirs(dirname(mp3_file)) - get_sentence = 'wget -q -U Mozilla -O' + mp3_file + \ - ' "https://translate.google.com/translate_tts?tl=' + \ - lang + '&q=' + sentence + '&client=tw-ob' + '"' - system(get_sentence) - return mp3_file +from ovos_utils.file_utils import resolve_resource_file def get_language_dir(base_path, lang="en-us"): @@ -67,4 +51,3 @@ def translate_word(name, lang='en-us'): except Exception: pass return name # use resource name as the word - diff --git a/ovos_utils/lang/detect.py b/ovos_utils/lang/detect.py deleted file mode 100644 index 00235799..00000000 --- a/ovos_utils/lang/detect.py +++ /dev/null @@ -1,36 +0,0 @@ -import requests - - -def detect_lang(text, return_dict=False, key=None, - url="https://libretranslate.com/detect"): - """host it yourself https://github.com/uav4geo/LibreTranslate""" - params = {"q": text} - if key: - params["api_key"] = key - res = requests.post(url, params=params).json() - if return_dict: - return res[0] - return res[0]["language"] - - -if __name__ == "__main__": - assert detect_lang("olá eu chamo-me joaquim") == "pt" - - assert detect_lang("olá eu chamo-me joaquim", return_dict=True) == \ - {'confidence': 0.9999939001351439, 'language': 'pt'} - - assert detect_lang("hello world") == "en" - - fr_en = """\ - France is the largest country in Western Europe and the third-largest in Europe as a whole. - A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections - et exporter Cet article concerne le pays européen aujourd’hui appelé République française. - Pour d’autres usages du nom France, Pour une aide rapide et effective, veuiller trouver votre aide - dans le menu ci-dessus. - Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. - The quick brown fox jumped over the lazy dog.""" - - assert detect_lang(fr_en) == "fr" - - assert detect_lang("This piece of text is in English. Този текст е на Български.", - return_dict=True) == {'confidence': 0.28571342657428966, 'language': 'en'} diff --git a/ovos_utils/lang/phonemes.py b/ovos_utils/lang/phonemes.py index 66bc9a27..b18c2262 100644 --- a/ovos_utils/lang/phonemes.py +++ b/ovos_utils/lang/phonemes.py @@ -1,26 +1,3 @@ -try: - from phoneme_guesser import guess_phonemes as _guess_phonemes, \ - get_phonemes as _get_phonemes - - - def guess_phonemes(word, lang="en-us"): - return _guess_phonemes(word, lang) - - - def get_phonemes(name, lang="en-us"): - return _get_phonemes(name, lang) - -except ImportError: - from ovos_utils.log import LOG - - LOG.warning("phoneme_guesser optional import is not available") - - def guess_phonemes(word, lang="en-us"): - raise ImportError("pip install phoneme_guesser") - - def get_phonemes(name, lang="en-us"): - raise ImportError("pip install phoneme_guesser") - ######################################################################## # ARPABET was invented for English. # The standard dictionary written in ARPABET is the CMU dictionary. diff --git a/ovos_utils/lang/translate.py b/ovos_utils/lang/translate.py deleted file mode 100644 index dc0731be..00000000 --- a/ovos_utils/lang/translate.py +++ /dev/null @@ -1,18 +0,0 @@ -from ovos_utils.lang.detect import detect_lang -import requests - - -def translate_text(text, lang="en-us", source_lang=None, - url="https://libretranslate.com/translate"): - """host it yourself https://github.com/uav4geo/LibreTranslate""" - lang = lang.split("-")[0] - if source_lang: - source_lang = source_lang.split("-")[0] - else: - source_lang = detect_lang(text) - r = requests.post(url, params={"q": text, - "source": source_lang, - "target": lang}).json() - if r.get("error"): - return None - return r["translatedText"] diff --git a/ovos_utils/parse.py b/ovos_utils/parse.py index cd2f6c5a..9d39b38f 100644 --- a/ovos_utils/parse.py +++ b/ovos_utils/parse.py @@ -99,59 +99,6 @@ def match_all(query, choices, match_func=None, strategy=MatchStrategy.SIMPLE_RAT return sorted(matches, key=lambda k: k[1], reverse=True) -def singularize(word, lang="en"): - return word.rstrip("s") - - -def split_sentences(text, new_lines=False): - if new_lines: - return text.split("\n") - - # normalize ambiguous cases - words = text.split(" ") - for idx, w in enumerate(words): - # prev_w = words[idx-1] if idx > 0 else "" - # next_w = words[idx + 1] if idx < len(words) - 1 else "" - if w == ".": - # handled ambiguous cases - # "hello . He said" # regex handles these next - - # ignored ambiguous cases - # "hello . he said" - pass - elif "." in w: - # ignored ambiguous cases - # "hello. he said" # could be "Jones Jr. thinks ..." - # "hello.he said" # could be "www.hello.com" - # "hellO.He said" # could be "A.E.I.O.U" - - # handled cases - # "hello.He said" - if len(w.split(".")) == 2: - prev_w, next_w = w.split(".") - if prev_w and next_w and prev_w[-1].islower() and \ - next_w[0].isupper(): - words[idx] = w.replace(".", ";") - text = " ".join(words) - # ignored ambiguous cases - # "hello. he said" # could be "Jones Jr. thinks ..." - # - - # handle punctuation delimiters except . - delims = ["\n", ";", "!", "?"] - _sentences = [s.strip() for s in re.split(r'(!|\?|\;|\n)*', text) if s not in - delims and s.strip()] - - sentences = [] - # handle . but be careful with numbers / names / websites? - for sent in _sentences: - sentences += [s.strip() for s in - re.split(r'(?<=[^A-Z].[.]) +(?=[A-Z])', sent) if - s.strip()] - - return sentences - - def remove_parentheses(answer): # remove [xx] (xx) {xx} answer = re.sub(r'\[[^)]*\]', '', answer) @@ -167,85 +114,3 @@ def remove_parentheses(answer): return None return answer - -def summarize(answer): - if not answer: - return None - return remove_parentheses(split_sentences(answer)[0]) - - -def search_in_text(query, text, lang="en", all_matches=False, - thresh=0.1, paragraphs=True): - words = query.split(" ") - for idx, word in enumerate(words): - words[idx] = singularize(word).lower().strip() - query = " ".join(words) - - # search text inside sections - candidates = split_sentences(text, paragraphs) - scores = [0 for i in range(len(candidates))] - for idx, c in enumerate(candidates): - if len(c.split(" ")) < 4: - continue - score = scores[idx] - for word in c.split(): - # total half assed scoring metric #1 - # each time query appears in sentence/paragraph boost score - if len(word) < 3: - continue - word = word.lower().strip() - singular_word = singularize(word, lang).lower().strip() - - if query == word: - # magic numbers are bad - score += 0.4 - elif query == singular_word: - # magic numbers are bad - score += 0.3 - # partial match - elif query in word or word in query: - score += 0.15 - elif query in singular_word or singular_word in query: - score += 0.1 - - scores[idx] = score - - # total half assed scoring metric #3 - # give preference to short sentences - if not paragraphs: - for idx, c in enumerate(candidates): - scores[idx] = scores[idx] / (len(c) / 200 + 0.3) - else: - for idx, c in enumerate(candidates): - scores[idx] = scores[idx] / (len(split_sentences(c)) + 0.1) - - best_score = max(scores) - - # this is a fake percent, sorry folks - if best_score > 1: - dif = best_score - 1 - scores = [s - dif for s in scores] - scores = [s if s > 0 else 0.0001 for s in scores] - best_score = 1 - - if not all_matches: - best = candidates[scores.index(best_score)] - return best, best_score - - data = [] - for idx, c in enumerate(candidates): - if c.strip() and scores[idx] >= thresh: - data.append((c, scores[idx])) - data.sort(key=lambda k: k[1], reverse=True) - return data - - -def extract_sentences(query, text, lang="en"): - return search_in_text(query, text, lang, - all_matches=True, paragraphs=False) - - -def extract_paragraphs(query, text, lang="en"): - return search_in_text(query, text, lang, - all_matches=True, paragraphs=True) - diff --git a/requirements/extras.txt b/requirements/extras.txt index 355f49c9..da2939a2 100644 --- a/requirements/extras.txt +++ b/requirements/extras.txt @@ -1,2 +1 @@ -colour~=0.1 rapidfuzz~=1.0 \ No newline at end of file diff --git a/test/unittests/test_color.py b/test/unittests/test_color.py deleted file mode 100644 index b937a129..00000000 --- a/test/unittests/test_color.py +++ /dev/null @@ -1,71 +0,0 @@ -import unittest -from ovos_utils.colors import Color, ColorOutOfSpace - - -class TestColorHelpers(unittest.TestCase): - @classmethod - def setUpClass(cls) -> None: - cls.red = Color.from_name("red") - cls.green = Color.from_name("green") - cls.light_blue = Color.from_name("light blue") - cls.white = Color.from_name("whitee") - cls.undefined_color = ColorOutOfSpace() - cls.unnamed_color = Color.from_rgb(120, 224, 4) - cls.artificial_color = Color.from_name("black swamp green") - cls.constructed = ColorOutOfSpace() - cls.constructed.set_blue(0.4) - cls.constructed.set_green(0.1) - cls.constructed.set_luminance(0.15) - - def test_color_formats(self): - self.assertEqual(self.white.hex, "#fff") - self.assertEqual(self.red.hex, "#f00") - - self.assertEqual(self.white.rgb255, (255, 255, 255)) - self.assertEqual(self.red.rgb255, (255, 0, 0)) - - self.assertEqual(self.red.hsv, (0.0, 1.0, 1.0)) - self.assertEqual(self.white.hsv, (0.0, 0.0, 1.0)) - - self.assertEqual(self.red.hls, (0.0, 0.5, 1.0)) - self.assertEqual(self.white.hls, (0.0, 1.0, 0.0)) - - def test_constructors(self): - self.assertEqual(self.white, Color("white")) - self.assertEqual(self.white, Color.from_rgb(255, 255, 255)) - self.assertEqual(self.white, Color.from_rgb_percent(1, 1, 1)) - self.assertEqual(self.white, Color.from_hex("#fff")) - # TODO FIXME - #self.assertEqual(self.white, Color.from_hsv(0.0, 0.0, 1.0)) - #self.assertEqual(self.white, Color.from_hls(0.0, 1.0, 0.0)) - - def test_color_properties(self): - self.assertEqual(self.red.name, "red") - self.assertEqual(self.red.color_description, "red") - self.assertEqual(self.light_blue.color_description, "light blue") - - self.assertEqual(self.unnamed_color.name, None) - self.assertEqual(self.unnamed_color.color_description, - "yellow-ish intense orange color") - self.assertEqual(self.unnamed_color.main_color, Color("orange")) - - self.assertEqual(self.artificial_color.hue, 0.33) - self.assertEqual(self.artificial_color.main_color, Color("gray")) - self.assertEqual(self.artificial_color.name, self.artificial_color.hex) - self.assertEqual(self.artificial_color.color_description, - "dark red-ish black-ish gray color") - - self.assertEqual(self.constructed.main_color, Color("black")) - self.assertEqual(self.constructed.color_description, - "dark blue-ish black-ish intense black color") - self.assertEqual(self.constructed.name, self.constructed.hex) - - def test_undefined_color(self): - empty_color = ColorOutOfSpace() - self.assertEqual(empty_color.name, empty_color.hex) - self.assertEqual(empty_color.hex, "#000") - self.assertEqual(empty_color.rgb255, (0, 0, 0)) - self.assertEqual(empty_color.color_description, "black") - self.assertEqual(empty_color.main_color, Color("gray")) - - diff --git a/test/unittests/test_parse.py b/test/unittests/test_parse.py deleted file mode 100644 index fb3c208e..00000000 --- a/test/unittests/test_parse.py +++ /dev/null @@ -1,108 +0,0 @@ -import unittest -from ovos_utils.parse import remove_parentheses, summarize, search_in_text, \ - extract_paragraphs, extract_sentences, split_sentences, singularize - - -class TestParseHelpers(unittest.TestCase): - @classmethod - def setUpClass(self): - self.test_string = "Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't.I know right\nOK" - self.wiki_dump = """Mycroft is a free and open-source voice assistant for Linux-based operating systems that uses a natural language user interface. Its code was formerly copyleft, but is now under a permissive license. - -History -Inspiration for Mycroft came when Ryan Sipes and Joshua Montgomery were visiting the Kansas City makerspace, where they came across a simple and basic intelligent virtual assistant project. They were interested in the technology, but did not like its inflexibility. Montgomery believes that the burgeoning industry of intelligent personal assistance poses privacy concerns for users and has promised that Mycroft will protect privacy through its open source machine learning platform. Mycroft has won several awards including the prestigious Techweek's KC Launch competition in 2016. Mycroft was part of the Sprint Accelerator 2016 class in Kansas City and joined 500 Startups Batch 20 in February 2017. The company accepted a strategic investment from Jaguar Land Rover during this same time period. To date, the company has raised more than $2.5 million from institutional investors and has opted to offer shares of the company to the public through Startengine, an equity crowdfunding platform. It is named after a fictional computer from 1966 science fiction novel The Moon Is a Harsh Mistress. - -Mycroft voice stack -Mycroft provides open source software for most parts of the voice stack. - -Wake Word -Mycroft does Wake Word spotting, also called keyword spotting, through its Precise Wake Word engine. Prior to Precise becoming the default Wake Word engine, Mycroft employed PocketSphinx. Instead of being based on phoneme recognition, Precise uses a trained recurrent neural network to distinguish between sounds which are, and which aren't Wake Words. - -Speech to text -Mycroft is partnering with Mozilla's Common Voice Project to leverage their DeepSpeech speech to text software. - -Intent parsing -Mycroft uses an intent parser called Adapt to convert natural language into machine-readable data structures. Adapt undertakes intent parsing by matching specific keywords in an order within an utterance. They also have a parser, Padatious. Padatious, in contrast, uses example-based inference to determine intent. - -Text to speech -For speech synthesis Mycroft uses Mimic, which is based on the Festival Lite speech synthesis system. - -Modular design and interoperability -Mycroft is designed to be modular, so users are able to change its components. For example, espeak can be used instead of Mimic. - -Hardware -The Mycroft project is also working on and selling smart speakers that run its software. All of its hardware is open-source, released under the CERN Open Hardware Licence.Its first hardware project was the Mark I, targeted primarily at developers. Its production was partially funded through a Kickstarter campaign, which finished successfully. Units started shipping out in April 2016.Its most recent hardware project is the Mark II, intended for general usage, not just for developers. Unlike the Mark I, the Mark II is equipped with a screen, being able to relay information both visually as well as acoustically. As with the Mark I, the Mark II's production will be partially funded through a Kickstarter campaign, which wrapped up in February 2018, hitting almost 8 times its original goal.Mycroft announced that a third hardware project, Mark III, will be offered through Kickstarter, and that an entire product line of Mark I, II, and III will be released to stores by November, 2019. - -Partnerships -Mycroft has undertaken several commercial collaborations. In May 2018, the company partnered with WorkAround, an impact sourcing provider who broker work opportunities for refugees, to undertake bulk machine learning training. In October 2018, Mycroft collaborated with disease surveillance and forecasting company, SickWeather, to identify the frequency of coughing on public transport, funded by the City of Kansas City, Missouri. - -See also - -Amazon Alexa -Cortana -Google Assistant -Siri - - -== References == - """ - - def test_singularize(self): - # uses inflection module for english - # https://pypi.org/project/inflection/ - self.assertEqual( - singularize("dogs"), "dog" - ) - self.assertEqual( - singularize("dogs", "en"), "dog" - ) - - # failure cases, just strips "s" - self.assertEqual( - singularize("cães", "pt"), "cãe" - ) - self.assertEqual( - singularize("cães", "es"), "cãe" - ) - - # TODO fix me - """ - def test_split_sentences(self): - # no split - self.assertEqual(split_sentences("A.E:I.O.U"), []) - self.assertEqual(split_sentences("hello.com"), []) - - # ambiguous, nosplit - self.assertEqual(split_sentences("hello. he said"), []) - self.assertEqual(split_sentences("hello.he said"), []) - # TODO maybe split this one? - self.assertEqual(split_sentences("hello . he said"), []) - - # ambiguous, but will split - self.assertEqual( - split_sentences("hello.He said"), - ["hello", "He said"] - ) - - # split at periods - self.assertEqual( - split_sentences("hello. He said"), - ["hello", "He said"] - ) - self.assertEqual( - split_sentences("hello . He said"), - ["hello", "He said"] - ) - - # TODO fix me - def test_summarize(self): - self.assertEqual( - extract_paragraphs("precise", self.wiki_dump), - [] - ) - self.assertEqual( - extract_sentences("intent", self.wiki_dump), - [] - ) - self.assertEqual(summarize(self.test_string), "") - """