From afe401c0de35935c27703128c4f623965ac1a80b Mon Sep 17 00:00:00 2001 From: nico Date: Sat, 15 Sep 2018 12:02:31 +0200 Subject: [PATCH] add google analytics stats --- Tests/settings/settings_test.yml | 5 +- Tests/test_models.py | 11 ++-- Tests/test_settings_loader.py | 4 +- docs/settings/settings.md | 15 +++++ .../ConfigurationManager/SettingLoader.py | 22 ++++++++ kalliope/core/HookManager.py | 9 +++ kalliope/core/Models/settings/Settings.py | 7 ++- kalliope/core/Utils/google_tracking.py | 56 +++++++++++++++++++ kalliope/settings.yml | 3 + 9 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 kalliope/core/Utils/google_tracking.py diff --git a/Tests/settings/settings_test.yml b/Tests/settings/settings_test.yml index d2e7e4c1..062534ea 100644 --- a/Tests/settings/settings_test.yml +++ b/Tests/settings/settings_test.yml @@ -118,4 +118,7 @@ var_files: options: deaf: True mute: False - energy_threshold: 3000 \ No newline at end of file + energy_threshold: 3000 + +# send hit to anonymously evaluate the global usage of Kalliope app by users +send_anonymous_usage_stats: False \ No newline at end of file diff --git a/Tests/test_models.py b/Tests/test_models.py index 067f65dd..07423327 100644 --- a/Tests/test_models.py +++ b/Tests/test_models.py @@ -265,7 +265,8 @@ def test_Settings(self): cache_path="/tmp/kalliope", resources=resources, variables={"key1": "val1"}, - options=options) + options=options, + send_anonymous_usage_stats=0) setting1.kalliope_version = "0.4.5" setting2 = Settings(default_tts_name="pico2wav", @@ -280,7 +281,8 @@ def test_Settings(self): cache_path="/tmp/kalliope", resources=resources, variables={"key1": "val1"}, - options=options) + options=options, + send_anonymous_usage_stats=0) setting3 = Settings(default_tts_name="pico2wav", default_stt_name="google", @@ -294,10 +296,11 @@ def test_Settings(self): cache_path="/tmp/kalliope", resources=resources, variables={"key1": "val1"}, - options=options) + options=options, + send_anonymous_usage_stats=0) setting3.kalliope_version = "0.4.5" - expected_result_serialize = {'default_tts_name': 'pico2wav', 'default_stt_name': 'google', 'default_trigger_name': 'swoyboy', 'default_player_name': 'mplayer', 'ttss': [{'name': 'tts1', 'parameters': {}}], 'stts': [{'name': 'stt1', 'parameters': {}}], 'triggers': [{'name': 'snowboy', 'parameters': {}}], 'players': [{'name': 'player1', 'parameters': None}], 'rest_api': {'password_protected': True, 'login': 'admin', 'password': 'password', 'active': True, 'port': 5000, 'allowed_cors_origin': '*'}, 'cache_path': '/tmp/kalliope', 'resources': {'neuron_folder': None, 'stt_folder': None, 'tts_folder': None, 'trigger_folder': None, 'signal_folder': None}, 'variables': {'key1': 'val1'}, 'machine': 'pumpkins', 'kalliope_version': '0.4.5', 'options': {'name': 'Options', 'energy_threshold': 4000, 'adjust_for_ambient_noise_second': 0, 'deaf': None, 'mute': None, 'stt_timeout': 0}, 'hooks': None} + expected_result_serialize = {'default_tts_name': 'pico2wav', 'default_stt_name': 'google', 'default_trigger_name': 'swoyboy', 'default_player_name': 'mplayer', 'ttss': [{'name': 'tts1', 'parameters': {}}], 'stts': [{'name': 'stt1', 'parameters': {}}], 'triggers': [{'name': 'snowboy', 'parameters': {}}], 'players': [{'name': 'player1', 'parameters': None}], 'rest_api': {'password_protected': True, 'login': 'admin', 'password': 'password', 'active': True, 'port': 5000, 'allowed_cors_origin': '*'}, 'cache_path': '/tmp/kalliope', 'resources': {'neuron_folder': None, 'stt_folder': None, 'tts_folder': None, 'trigger_folder': None, 'signal_folder': None}, 'variables': {'key1': 'val1'}, 'machine': 'pumpkins', 'kalliope_version': '0.4.5', 'options': {'name': 'Options', 'energy_threshold': 4000, 'adjust_for_ambient_noise_second': 0, 'deaf': None, 'mute': None, 'stt_timeout': 0}, 'hooks': None, 'send_anonymous_usage_stats': 0} self.maxDiff = None self.assertDictEqual(expected_result_serialize, setting1.serialize()) diff --git a/Tests/test_settings_loader.py b/Tests/test_settings_loader.py index 464b6240..13139a6d 100644 --- a/Tests/test_settings_loader.py +++ b/Tests/test_settings_loader.py @@ -70,7 +70,8 @@ def setUp(self): 'on_processed_synapses': None, 'on_start_speaking': None, 'on_stop_speaking': None - } + }, + 'send_anonymous_usage_stats': 0 } # Init the folders, otherwise it raises an exceptions @@ -146,6 +147,7 @@ def test_get_settings(self): 'on_start_speaking': None, 'on_stop_speaking': None, } + settings_object.send_anonymous_usage_stats = 0 sl = SettingLoader(file_path=self.settings_file_to_test) diff --git a/docs/settings/settings.md b/docs/settings/settings.md index 11de87e5..6191c95e 100644 --- a/docs/settings/settings.md +++ b/docs/settings/settings.md @@ -577,3 +577,18 @@ options: ``` >**Note:** The number of second here represents the time between kalliope's awakening and the moment when you can give her your order. + + +## send_anonymous_usage_stats + +This flag allow Kalliope to send some stats in order to anonymously evaluate the global usage of Kalliope app by users. + +Syntax: +```yml +send_anonymous_usage_stats: +``` + +E.g: +```yml +send_anonymous_usage_stats: False +``` diff --git a/kalliope/core/ConfigurationManager/SettingLoader.py b/kalliope/core/ConfigurationManager/SettingLoader.py index 749de1ae..64d06663 100644 --- a/kalliope/core/ConfigurationManager/SettingLoader.py +++ b/kalliope/core/ConfigurationManager/SettingLoader.py @@ -1,5 +1,7 @@ import logging import os +import uuid + from six import with_metaclass from kalliope.core.Models.settings.Options import Options @@ -113,6 +115,7 @@ def _get_settings(self): variables = self._get_variables(settings) options = self._get_options(settings) hooks = self._get_hooks(settings) + send_anonymous_usage_stats = self._get_anonymous_usage_stats(settings) # Load the setting singleton with the parameters setting_object.default_tts_name = default_tts_name @@ -129,6 +132,7 @@ def _get_settings(self): setting_object.variables = variables setting_object.options = options setting_object.hooks = hooks + setting_object.send_anonymous_usage_stats = send_anonymous_usage_stats return setting_object @@ -685,3 +689,21 @@ def _get_hooks(settings): hooks[key] = None return hooks + + def _get_anonymous_usage_stats(self, settings): + + cid = uuid.uuid4().hex + try: + send_anonymous_usage_stats = settings["send_anonymous_usage_stats"] + bool_send_anonymous_usage_stats = Utils.str_to_bool(send_anonymous_usage_stats) + if bool_send_anonymous_usage_stats: + # generate a unique user ID + send_anonymous_usage_stats = cid + else: # the user choose to disable stats + send_anonymous_usage_stats = 0 + + except KeyError: + # if the user haven't set this flag + send_anonymous_usage_stats = cid + logger.debug("[SettingsLoader] send_anonymous_usage_stats: %s" % send_anonymous_usage_stats) + return send_anonymous_usage_stats diff --git a/kalliope/core/HookManager.py b/kalliope/core/HookManager.py index 5380bb4d..e98d2159 100644 --- a/kalliope/core/HookManager.py +++ b/kalliope/core/HookManager.py @@ -1,6 +1,9 @@ +from kalliope._version import version_str from kalliope.core.ConfigurationManager import SettingLoader import logging +from kalliope.core.Utils.google_tracking import GoogleTracking + logging.basicConfig() logger = logging.getLogger("kalliope") @@ -17,6 +20,12 @@ def on_waiting_for_trigger(cls): @classmethod def on_triggered(cls): + sl = SettingLoader() + gt = GoogleTracking(cid=sl.settings.send_anonymous_usage_stats, + kalliope_version=version_str, + category='synapse', + action='execute') + gt.start() return cls.execute_synapses_in_hook_name("on_triggered") @classmethod diff --git a/kalliope/core/Models/settings/Settings.py b/kalliope/core/Models/settings/Settings.py index 9da69c4c..25069806 100644 --- a/kalliope/core/Models/settings/Settings.py +++ b/kalliope/core/Models/settings/Settings.py @@ -22,7 +22,8 @@ def __init__(self, resources=None, variables=None, options=None, - hooks=None): + hooks=None, + send_anonymous_usage_stats=None): self.default_tts_name = default_tts_name self.default_stt_name = default_stt_name @@ -40,6 +41,7 @@ def __init__(self, self.kalliope_version = current_kalliope_version self.options = options self.hooks = hooks + self.send_anonymous_usage_stats = send_anonymous_usage_stats def serialize(self): """ @@ -64,7 +66,8 @@ def serialize(self): 'machine': self.machine, 'kalliope_version': self.kalliope_version, 'options': self.options.serialize(), - 'hooks': self.hooks + 'hooks': self.hooks, + 'send_anonymous_usage_stats': self.send_anonymous_usage_stats } def __str__(self): diff --git a/kalliope/core/Utils/google_tracking.py b/kalliope/core/Utils/google_tracking.py new file mode 100644 index 00000000..a42f73ba --- /dev/null +++ b/kalliope/core/Utils/google_tracking.py @@ -0,0 +1,56 @@ +import logging +from threading import Thread + +import requests + +GA_TRACKING_ID = "UA-124800612-1" + +logging.basicConfig() +logger = logging.getLogger("kalliope") +logger.setLevel(logging.DEBUG) + + +class GoogleTracking(Thread): + """ + send hit to Google Analytics + allow to anonymously evaluate the global usage of Kalliope app by users + """ + + def __init__(self, **kwargs): + super(GoogleTracking, self).__init__() + self.category = kwargs.get("category") + self.action = kwargs.get("action") + self.label = kwargs.get("label", None) + self.value = kwargs.get("value", 0) + self.kalliope_version = kwargs.get("kalliope_version", 0) + + def run(self): + self.track_event(self.kalliope_version, self.category, self.action, self.label, self.value) + + @staticmethod + def track_event(cid, kalliope_version, category, action, label=None, value=0): + # allowed parameters: https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters + data = { + 'v': '1', # API Version. + 'tid': GA_TRACKING_ID, # Tracking ID / Property ID. + 'cid': cid, # unique user id + 'an': "kalliope", + 'av': kalliope_version, + 'ds': 'api', + 't': 'event', # Event hit type. + 'ec': category, # Event category. + 'ea': action, # Event action. + 'el': label, # Event label. + 'ev': value, # Event value, must be an integer + } + try: + response = requests.post( + 'http://www.google-analytics.com/collect', data=data) + + # If the request fails, this will raise a RequestException. + response.raise_for_status() + + logger.debug("[GoogleTracking] hit sent: %s" % response.status_code) + except Exception as e: + logger.debug("[GoogleTracking] fail to send data: %s" % e) + diff --git a/kalliope/settings.yml b/kalliope/settings.yml index 8c16e09f..ce576469 100644 --- a/kalliope/settings.yml +++ b/kalliope/settings.yml @@ -166,3 +166,6 @@ options: # stt_timeout: 5 # Speech to text option deaf: False mute: False + +# send hit to anonymously evaluate the global usage of Kalliope app by users +send_anonymous_usage_stats: False