diff --git a/CHANGELOG.md b/CHANGELOG.md index 86fdf5dd..a927f8dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,52 +1,52 @@ # Changelog -## [1.7.0](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.0) (2023-10-26) +## [1.7.1a6](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.1a6) (2023-11-27) -[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a5...1.7.0) +[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.7.1a5...1.7.1a6) -**Fixed bugs:** +**Merged pull requests:** -- \[BUG\] Got some error/warning when the skill initialize [\#463](https://github.com/NeonGeckoCom/neon-utils/issues/463) +- Ensure skill responses to API inputs [\#487](https://github.com/NeonGeckoCom/neon-utils/pull/487) ([NeonDaniel](https://github.com/NeonDaniel)) -## [1.6.3a5](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a5) (2023-10-25) +## [1.7.1a5](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.1a5) (2023-11-27) -[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a4...1.6.3a5) +[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.7.1a4...1.7.1a5) **Merged pull requests:** -- Troubleshooting Skill Class Init [\#481](https://github.com/NeonGeckoCom/neon-utils/pull/481) ([NeonDaniel](https://github.com/NeonDaniel)) +- Troubleshoot Location test failures [\#488](https://github.com/NeonGeckoCom/neon-utils/pull/488) ([NeonDaniel](https://github.com/NeonDaniel)) -## [1.6.3a4](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a4) (2023-10-24) +## [1.7.1a4](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.1a4) (2023-11-17) -[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a3...1.6.3a4) +[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.7.1a3...1.7.1a4) **Merged pull requests:** -- Update Skill base classes [\#480](https://github.com/NeonGeckoCom/neon-utils/pull/480) ([NeonDaniel](https://github.com/NeonDaniel)) +- Fix bug in default metrics bus handling [\#486](https://github.com/NeonGeckoCom/neon-utils/pull/486) ([NeonDaniel](https://github.com/NeonDaniel)) -## [1.6.3a3](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a3) (2023-10-10) +## [1.7.1a3](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.1a3) (2023-11-17) -[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a2...1.6.3a3) +[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.7.1a2...1.7.1a3) **Merged pull requests:** -- Better exception handling around signal method patching [\#479](https://github.com/NeonGeckoCom/neon-utils/pull/479) ([NeonDaniel](https://github.com/NeonDaniel)) +- Implement stopwatch metrics reporting with unit test [\#485](https://github.com/NeonGeckoCom/neon-utils/pull/485) ([NeonDaniel](https://github.com/NeonDaniel)) -## [1.6.3a2](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a2) (2023-10-10) +## [1.7.1a2](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.1a2) (2023-11-16) -[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.3a1...1.6.3a2) +[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.7.1a1...1.7.1a2) **Merged pull requests:** -- Skill Compatibility [\#478](https://github.com/NeonGeckoCom/neon-utils/pull/478) ([NeonDaniel](https://github.com/NeonDaniel)) +- Patch multiple native languages intent registration [\#484](https://github.com/NeonGeckoCom/neon-utils/pull/484) ([NeonDaniel](https://github.com/NeonDaniel)) -## [1.6.3a1](https://github.com/NeonGeckoCom/neon-utils/tree/1.6.3a1) (2023-10-10) +## [1.7.1a1](https://github.com/NeonGeckoCom/neon-utils/tree/1.7.1a1) (2023-11-10) -[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.6.2...1.6.3a1) +[Full Changelog](https://github.com/NeonGeckoCom/neon-utils/compare/1.7.0...1.7.1a1) **Merged pull requests:** -- Emit handler complete event for CommonQuery skills [\#477](https://github.com/NeonGeckoCom/neon-utils/pull/477) ([NeonDaniel](https://github.com/NeonDaniel)) +- Fix extra metrics utils import [\#483](https://github.com/NeonGeckoCom/neon-utils/pull/483) ([NeonDaniel](https://github.com/NeonDaniel)) diff --git a/neon_utils/location_utils.py b/neon_utils/location_utils.py index d052999a..6cbb721a 100644 --- a/neon_utils/location_utils.py +++ b/neon_utils/location_utils.py @@ -38,6 +38,7 @@ from ovos_utils.log import LOG +# geocode.maps.co nominatim.openstreetmap.org _NOMINATIM_DOMAIN = "geocode.maps.co" diff --git a/neon_utils/message_utils.py b/neon_utils/message_utils.py index b3c59c91..3bc7b003 100644 --- a/neon_utils/message_utils.py +++ b/neon_utils/message_utils.py @@ -294,4 +294,7 @@ def neon_must_respond(message: Message = None) -> bool: else: # Solo Private return True + if message.context.get("client") in ("mana", "mq_api"): + # API requests should always return a response + return True return False diff --git a/neon_utils/metrics_utils.py b/neon_utils/metrics_utils.py index 42c9dfdb..4fc53b8e 100644 --- a/neon_utils/metrics_utils.py +++ b/neon_utils/metrics_utils.py @@ -28,27 +28,28 @@ from socket import gethostname from time import time, strftime +from ovos_bus_client import Message, MessageBusClient +from ovos_utils.log import LOG -from neon_utils.logger import LOG -from neon_utils.mq_utils import send_mq_request +from neon_utils.message_utils import dig_for_message -# TODO: Enable metric reporting DM class Stopwatch: """ Provides a stopwatch object compatible with mycroft.metrics Stopwatch. """ - def __init__(self, metric_name=None, allow_reporting=False): + def __init__(self, metric_name=None, allow_reporting=False, bus=None): """ Create a stopwatch object with an optional metric_name - Args: - metric_name: name of the metric this stopwatch is measuring - allow_reporting: boolean flag to allow this stopwatch to report measured metrics + @param metric_name: name of the metric this stopwatch is measuring + @param allow_reporting: boolean flag to allow this stopwatch to report measured metrics + @param bus: MessageBusClient object to report metrics with """ self._metric = metric_name self._report = allow_reporting self._context = dict() + self._bus = bus self.start_time = None self.time = None @@ -57,16 +58,10 @@ def __enter__(self): def __exit__(self, typ, val, traceback): self.stop() - # self.report() - - # def add_context(self, context: dict): - # """ - # Add context to the measured metric. - # Args: - # context: dict of arbitrary data to add to this metric reporting - # """ - # if self._metric: - # self._context = context + try: + self.report() + except Exception as e: + LOG.exception(e) def start(self): self.start_time = time() @@ -75,10 +70,16 @@ def stop(self): self.time = time() - self.start_time return self.time - # def report(self): - # if self._metric and self._report: - # report_metric(self._metric, self._context) - # self._context = dict() + def report(self): + if self._metric and self._report: + if not self._bus: + self._bus = MessageBusClient() + self._bus.run_in_thread() + message = dig_for_message() or Message("") + message.context['timestamp'] = time() + self._bus.emit(message.forward("neon.metric", + {"name": self._metric, + "duration": self.time})) def report_metric(name: str, **kwargs): @@ -87,7 +88,9 @@ def report_metric(name: str, **kwargs): :param name: Name of the metric to report :param kwargs: Arbitrary data to include with metric report """ + # TODO: Deprecate and move to PHAL plugin try: + from neon_utils.mq_utils import send_mq_request send_mq_request("/neon_metrics", {**{"name": name}, **kwargs}, "neon_metrics_input", expect_response=False) return True @@ -98,6 +101,7 @@ def report_metric(name: str, **kwargs): def announce_connection(): try: + from neon_utils.mq_utils import send_mq_request from neon_core.version import __version__ from ovos_config.config import Configuration data = {"time": strftime('%Y-%m-%d %H:%M:%S'), diff --git a/neon_utils/skills/neon_skill.py b/neon_utils/skills/neon_skill.py index b35ceedf..c6ebf12d 100644 --- a/neon_utils/skills/neon_skill.py +++ b/neon_utils/skills/neon_skill.py @@ -892,3 +892,14 @@ def init_dialog(self, root_directory: Optional[str] = None): """ log_deprecation("Use `load_dialog_files`", "2.0.0") self.load_dialog_files(root_directory) + + def add_event(self, name: str, handler: callable, + handler_info: Optional[str] = None, once: bool = False, + speak_errors: bool = True): + # TODO: Remove with ovos-workshop==0.0.13 + if handler_info == "mycroft.skill.handler" and \ + self.bus.emitter.listeners(name): + LOG.warning(f"Not re-registering intent handler {name}") + return + BaseSkill.add_event(self, name, handler, handler_info, once, + speak_errors) diff --git a/tests/location_util_tests.py b/tests/location_util_tests.py index dd2898eb..aeb641b9 100644 --- a/tests/location_util_tests.py +++ b/tests/location_util_tests.py @@ -29,6 +29,8 @@ import unittest from datetime import datetime +from time import sleep + from dateutil.tz import gettz, tzlocal @@ -41,23 +43,27 @@ def test_get_coordinates_complete(self): "country": "United States"}) self.assertIsInstance(coords[0], float) self.assertIsInstance(coords[1], float) + sleep(1) # maps.co rate-limit # No city specified coords = get_coordinates({"state": "Washington", "country": "United States"}) self.assertIsInstance(coords[0], float) self.assertIsInstance(coords[1], float) + sleep(1) # maps.co rate-limit # No state specified coords = get_coordinates({"city": "Seattle", "country": "United States"}) self.assertIsInstance(coords[0], float) self.assertIsInstance(coords[1], float) + sleep(1) # maps.co rate-limit # No country specified coords = get_coordinates({"state": "Washington", "city": "Renton"}) self.assertIsInstance(coords[0], float) self.assertIsInstance(coords[1], float) + sleep(1) # maps.co rate-limit def test_get_location_from_coords(self): from neon_utils.location_utils import get_location @@ -106,6 +112,7 @@ def test_get_full_location(self): self.assertEqual(location_en['address']['state'], "Washington") self.assertEqual(location_en['address']['country'], "United States") self.assertEqual(location_en['address']['country_code'], "us") + sleep(1) # maps.co rate-limit location_es = get_full_location("Seattle, Washington", "es") self.assertEqual(location_es['lat'], location_en['lat']) @@ -113,6 +120,7 @@ def test_get_full_location(self): self.assertEqual(location_es['address']['country'], "Estados Unidos de América") self.assertEqual(location_en['address']['country_code'], "us") + sleep(1) # maps.co rate-limit location_from_coords = get_full_location((location_en['lat'], location_en['lon'])) diff --git a/tests/message_util_tests.py b/tests/message_util_tests.py index 0573ccc3..77f86d99 100644 --- a/tests/message_util_tests.py +++ b/tests/message_util_tests.py @@ -277,12 +277,17 @@ def test_neon_must_respond(self): "conversation with Neon"}, {"klat_data": { "title": "!PRIVATE:user"}}) + mq_api_message = Message("", {}, {"client": "mq_api"}) + mana_api_message = Message("", {}, {"client": "mana"}) + self.assertFalse(neon_must_respond()) self.assertTrue(neon_must_respond(private_message_solo)) self.assertTrue(neon_must_respond(private_message_neon)) self.assertFalse(neon_must_respond(private_message_neon_plus)) self.assertFalse(neon_must_respond(public_message)) self.assertFalse(neon_must_respond(first_message)) + self.assertTrue(neon_must_respond(mq_api_message)) + self.assertTrue(neon_must_respond(mana_api_message)) if __name__ == '__main__': diff --git a/tests/metric_util_tests.py b/tests/metric_util_tests.py index ee0f2028..bde9c64b 100644 --- a/tests/metric_util_tests.py +++ b/tests/metric_util_tests.py @@ -31,6 +31,7 @@ import sys import os import unittest +from unittest.mock import Mock sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) from neon_utils.metrics_utils import * @@ -76,6 +77,20 @@ def test_stopwatch_init_params(self): self.assertIsNone(stopwatch._metric) self.assertFalse(stopwatch._report) + def test_stopwatch_report_metric(self): + from ovos_utils.messagebus import FakeBus + bus = FakeBus() + on_metric = Mock() + bus.on("neon.metric", on_metric) + stopwatch = Stopwatch("test", True, bus) + with stopwatch: + sleep(0.05) + on_metric.assert_called_once() + msg = on_metric.call_args[0][0] + self.assertEqual(msg.data['name'], "test") + self.assertIsInstance(msg.data['duration'], float) + self.assertIsInstance(msg.context['timestamp'], float) + def test_report_metric(self): self.assertTrue(report_metric("test", data="this is only a test")) diff --git a/version.py b/version.py index 0375d6a6..cdecb602 100644 --- a/version.py +++ b/version.py @@ -26,4 +26,4 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -__version__ = "1.7.0" +__version__ = "1.8.0"