SettingLoader.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. import logging
  2. import os
  3. from YAMLLoader import YAMLLoader
  4. from kalliope.core.Models.Resources import Resources
  5. from kalliope.core.Utils.Utils import Utils
  6. from kalliope.core.Models import Singleton
  7. from kalliope.core.Models.RestAPI import RestAPI
  8. from kalliope.core.Models.Settings import Settings
  9. from kalliope.core.Models.Stt import Stt
  10. from kalliope.core.Models.Trigger import Trigger
  11. from kalliope.core.Models.Tts import Tts
  12. from kalliope.core.Utils.FileManager import FileManager
  13. FILE_NAME = "settings.yml"
  14. logging.basicConfig()
  15. logger = logging.getLogger("kalliope")
  16. class SettingInvalidException(Exception):
  17. """
  18. Some data must match the expected value/type
  19. .. seealso:: Settings
  20. """
  21. pass
  22. class NullSettingException(Exception):
  23. """
  24. Some Attributes can not be Null
  25. .. seealso:: Settings
  26. """
  27. pass
  28. class SettingNotFound(Exception):
  29. """
  30. Some Attributes are missing
  31. .. seealso:: Settings
  32. """
  33. pass
  34. class SettingLoader(object):
  35. """
  36. This Class is used to get the Settings YAML and the Settings as an object
  37. """
  38. __metaclass__ = Singleton
  39. def __init__(self, file_path=None):
  40. self.file_path = file_path
  41. if self.file_path is None:
  42. self.file_path = Utils.get_real_file_path(FILE_NAME)
  43. else:
  44. self.file_path = Utils.get_real_file_path(file_path)
  45. # if the returned file path is none, the file doesn't exist
  46. if self.file_path is None:
  47. raise SettingNotFound("Settings.yml file not found")
  48. self.yaml_config = self._get_yaml_config()
  49. self.settings = self._get_settings()
  50. def _get_yaml_config(self):
  51. """
  52. Class Methods which loads default or the provided YAML file and return it as a String
  53. :return: The loaded settings YAML
  54. :rtype: dict
  55. :Example:
  56. settings_yaml = SettingLoader.get_yaml_config(/var/tmp/settings.yml)
  57. .. warnings:: Class Method
  58. """
  59. return YAMLLoader.get_config(self.file_path)
  60. def _get_settings(self):
  61. """
  62. Class Methods which loads default or the provided YAML file and return a Settings Object
  63. :return: The loaded Settings
  64. :rtype: Settings
  65. :Example:
  66. settings = SettingLoader.get_settings(file_path="/var/tmp/settings.yml")
  67. .. seealso:: Settings
  68. .. warnings:: Class Method
  69. """
  70. # create a new setting
  71. setting_object = Settings()
  72. # Get the setting parameters
  73. settings = self._get_yaml_config()
  74. default_stt_name = self._get_default_speech_to_text(settings)
  75. default_tts_name = self._get_default_text_to_speech(settings)
  76. default_trigger_name = self._get_default_trigger(settings)
  77. stts = self._get_stts(settings)
  78. ttss = self._get_ttss(settings)
  79. triggers = self._get_triggers(settings)
  80. random_wake_up_answers = self._get_random_wake_up_answers(settings)
  81. random_wake_up_sound = self._get_random_wake_up_sounds(settings)
  82. play_on_ready_notification = self._get_play_on_ready_notification(settings)
  83. on_ready_answers = self._get_on_ready_answers(settings)
  84. on_ready_sounds = self._get_on_ready_sounds(settings)
  85. rest_api = self._get_rest_api(settings)
  86. cache_path = self._get_cache_path(settings)
  87. default_synapse = self._get_default_synapse(settings)
  88. resources = self._get_resources(settings)
  89. # Load the setting singleton with the parameters
  90. setting_object.default_tts_name = default_tts_name
  91. setting_object.default_stt_name = default_stt_name
  92. setting_object.default_trigger_name = default_trigger_name
  93. setting_object.stts = stts
  94. setting_object.ttss = ttss
  95. setting_object.triggers = triggers
  96. setting_object.random_wake_up_answers = random_wake_up_answers
  97. setting_object.random_wake_up_sounds = random_wake_up_sound
  98. setting_object.play_on_ready_notification = play_on_ready_notification
  99. setting_object.on_ready_answers = on_ready_answers
  100. setting_object.on_ready_sounds = on_ready_sounds
  101. setting_object.rest_api = rest_api
  102. setting_object.cache_path = cache_path
  103. setting_object.default_synapse = default_synapse
  104. setting_object.resources = resources
  105. return setting_object
  106. @staticmethod
  107. def _get_default_speech_to_text(settings):
  108. """
  109. Get the default speech to text defined in the settings.yml file
  110. :param settings: The YAML settings file
  111. :type settings: dict
  112. :return: the default speech to text
  113. :rtype: str
  114. :Example:
  115. default_stt_name = cls._get_default_speech_to_text(settings)
  116. .. seealso:: Stt
  117. .. raises:: NullSettingException, SettingNotFound
  118. .. warnings:: Static and Private
  119. """
  120. try:
  121. default_speech_to_text = settings["default_speech_to_text"]
  122. if default_speech_to_text is None:
  123. raise NullSettingException("Attribute default_speech_to_text is null")
  124. logger.debug("Default STT: %s" % default_speech_to_text)
  125. return default_speech_to_text
  126. except KeyError, e:
  127. raise SettingNotFound("%s setting not found" % e)
  128. @staticmethod
  129. def _get_default_text_to_speech(settings):
  130. """
  131. Get the default text to speech defined in the settings.yml file
  132. :param settings: The YAML settings file
  133. :type settings: dict
  134. :return: the default text to speech
  135. :rtype: str
  136. :Example:
  137. default_tts_name = cls._get_default_text_to_speech(settings)
  138. .. seealso:: Tts
  139. .. raises:: NullSettingException, SettingNotFound
  140. .. warnings:: Static and Private
  141. """
  142. try:
  143. default_text_to_speech = settings["default_text_to_speech"]
  144. if default_text_to_speech is None:
  145. raise NullSettingException("Attribute default_text_to_speech is null")
  146. logger.debug("Default TTS: %s" % default_text_to_speech)
  147. return default_text_to_speech
  148. except KeyError, e:
  149. raise SettingNotFound("%s setting not found" % e)
  150. @staticmethod
  151. def _get_default_trigger(settings):
  152. """
  153. Get the default trigger defined in the settings.yml file
  154. :param settings: The YAML settings file
  155. :type settings: dict
  156. :return: the default trigger
  157. :rtype: str
  158. :Example:
  159. default_trigger_name = cls._get_default_trigger(settings)
  160. .. seealso:: Trigger
  161. .. raises:: NullSettingException, SettingNotFound
  162. .. warnings:: Static and Private
  163. """
  164. try:
  165. default_trigger = settings["default_trigger"]
  166. if default_trigger is None:
  167. raise NullSettingException("Attribute default_trigger is null")
  168. logger.debug("Default Trigger name: %s" % default_trigger)
  169. return default_trigger
  170. except KeyError, e:
  171. raise SettingNotFound("%s setting not found" % e)
  172. @staticmethod
  173. def _get_stts(settings):
  174. """
  175. Return a list of stt object
  176. :param settings: The YAML settings file
  177. :type settings: dict
  178. :return: List of Stt
  179. :rtype: list
  180. :Example:
  181. stts = cls._get_stts(settings)
  182. .. seealso:: Stt
  183. .. raises:: SettingNotFound
  184. .. warnings:: Class Method and Private
  185. """
  186. try:
  187. speechs_to_text_list = settings["speech_to_text"]
  188. except KeyError:
  189. raise SettingNotFound("speech_to_text settings not found")
  190. stts = list()
  191. for speechs_to_text_el in speechs_to_text_list:
  192. if isinstance(speechs_to_text_el, dict):
  193. # print "Neurons dict ok"
  194. for stt_name in speechs_to_text_el:
  195. name = stt_name
  196. parameters = speechs_to_text_el[name]
  197. new_stt = Stt(name=name, parameters=parameters)
  198. stts.append(new_stt)
  199. else:
  200. # the stt does not have parameter
  201. new_stt = Stt(name=speechs_to_text_el, parameters=dict())
  202. stts.append(new_stt)
  203. return stts
  204. @staticmethod
  205. def _get_ttss(settings):
  206. """
  207. Return a list of stt object
  208. :param settings: The YAML settings file
  209. :type settings: dict
  210. :return: List of Ttss
  211. :rtype: list
  212. :Example:
  213. ttss = cls._get_ttss(settings)
  214. .. seealso:: Tts
  215. .. raises:: SettingNotFound
  216. .. warnings:: Class Method and Private
  217. """
  218. try:
  219. text_to_speech_list = settings["text_to_speech"]
  220. except KeyError, e:
  221. raise SettingNotFound("%s setting not found" % e)
  222. ttss = list()
  223. for text_to_speech_el in text_to_speech_list:
  224. if isinstance(text_to_speech_el, dict):
  225. # print "Neurons dict ok"
  226. for tts_name in text_to_speech_el:
  227. name = tts_name
  228. parameters = text_to_speech_el[name]
  229. new_tts = Tts(name=name, parameters=parameters)
  230. ttss.append(new_tts)
  231. else:
  232. # the neuron does not have parameter
  233. new_tts = Tts(name=text_to_speech_el)
  234. ttss.append(new_tts)
  235. return ttss
  236. @staticmethod
  237. def _get_triggers(settings):
  238. """
  239. Return a list of Trigger object
  240. :param settings: The YAML settings file
  241. :type settings: dict
  242. :return: List of Trigger
  243. :rtype: list
  244. :Example:
  245. triggers = cls._get_triggers(settings)
  246. .. seealso:: Trigger
  247. .. raises:: SettingNotFound
  248. .. warnings:: Class Method and Private
  249. """
  250. try:
  251. triggers_list = settings["triggers"]
  252. except KeyError, e:
  253. raise SettingNotFound("%s setting not found" % e)
  254. triggers = list()
  255. for trigger_el in triggers_list:
  256. if isinstance(trigger_el, dict):
  257. # print "Neurons dict ok"
  258. for trigger_name in trigger_el:
  259. name = trigger_name
  260. parameters = trigger_el[name]
  261. new_trigger = Trigger(name=name, parameters=parameters)
  262. triggers.append(new_trigger)
  263. else:
  264. # the neuron does not have parameter
  265. new_trigger = Trigger(name=trigger_el)
  266. triggers.append(new_trigger)
  267. return triggers
  268. @staticmethod
  269. def _get_random_wake_up_answers(settings):
  270. """
  271. Return a list of the wake up answers set up on the settings.yml file
  272. :param settings: The YAML settings file
  273. :type settings: dict
  274. :return: List of wake up answers
  275. :rtype: list of str
  276. :Example:
  277. wakeup = cls._get_random_wake_up_answers(settings)
  278. .. seealso::
  279. .. raises:: NullSettingException
  280. .. warnings:: Class Method and Private
  281. """
  282. try:
  283. random_wake_up_answers_list = settings["random_wake_up_answers"]
  284. except KeyError:
  285. # User does not provide this settings
  286. return None
  287. # The list cannot be empty
  288. if random_wake_up_answers_list is None:
  289. raise NullSettingException("random_wake_up_answers settings is null")
  290. return random_wake_up_answers_list
  291. @staticmethod
  292. def _get_random_wake_up_sounds(settings):
  293. """
  294. Return a list of the wake up sounds set up on the settings.yml file
  295. :param settings: The YAML settings file
  296. :type settings: dict
  297. :return: list of wake up sounds
  298. :rtype: list of str
  299. :Example:
  300. wakeup_sounds = cls._get_random_wake_up_sounds(settings)
  301. .. seealso::
  302. .. raises:: NullSettingException
  303. .. warnings:: Class Method and Private
  304. """
  305. try:
  306. random_wake_up_sounds_list = settings["random_wake_up_sounds"]
  307. # In case files are declared in settings.yml, make sure kalliope can access them.
  308. for sound in random_wake_up_sounds_list:
  309. if Utils.get_real_file_path(sound) is None:
  310. raise SettingInvalidException("sound file %s not found" % sound)
  311. except KeyError:
  312. # User does not provide this settings
  313. return None
  314. # The the setting is present, the list cannot be empty
  315. if random_wake_up_sounds_list is None:
  316. raise NullSettingException("random_wake_up_sounds settings is empty")
  317. return random_wake_up_sounds_list
  318. @staticmethod
  319. def _get_rest_api(settings):
  320. """
  321. Return the settings of the RestApi
  322. :param settings: The YAML settings file
  323. :type settings: dict
  324. :return: the RestApi object
  325. :rtype: RestApi
  326. :Example:
  327. rest_api = cls._get_rest_api(settings)
  328. .. seealso:: RestApi
  329. .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
  330. .. warnings:: Class Method and Private
  331. """
  332. try:
  333. rest_api = settings["rest_api"]
  334. except KeyError, e:
  335. raise SettingNotFound("%s setting not found" % e)
  336. if rest_api is not None:
  337. try:
  338. password_protected = rest_api["password_protected"]
  339. if password_protected is None:
  340. raise NullSettingException("password_protected setting cannot be null")
  341. login = rest_api["login"]
  342. password = rest_api["password"]
  343. if password_protected:
  344. if login is None:
  345. raise NullSettingException("login setting cannot be null if password_protected is True")
  346. if password is None:
  347. raise NullSettingException("password setting cannot be null if password_protected is True")
  348. active = rest_api["active"]
  349. if active is None:
  350. raise NullSettingException("active setting cannot be null")
  351. port = rest_api["port"]
  352. if port is None:
  353. raise NullSettingException("port setting cannot be null")
  354. # check that the port in an integer
  355. try:
  356. port = int(port)
  357. except ValueError:
  358. raise SettingInvalidException("port must be an integer")
  359. # check the port is a valid port number
  360. if not 1024 <= port <= 65535:
  361. raise SettingInvalidException("port must be in range 1024-65535")
  362. # check the CORS request settings
  363. allowed_cors_origin = False
  364. if "allowed_cors_origin" in rest_api:
  365. allowed_cors_origin = rest_api["allowed_cors_origin"]
  366. except KeyError, e:
  367. # print e
  368. raise SettingNotFound("%s settings not found" % e)
  369. # config ok, we can return the rest api object
  370. rest_api_obj = RestAPI(password_protected=password_protected, login=login, password=password,
  371. active=active, port=port, allowed_cors_origin=allowed_cors_origin)
  372. return rest_api_obj
  373. else:
  374. raise NullSettingException("rest_api settings cannot be null")
  375. @staticmethod
  376. def _get_cache_path(settings):
  377. """
  378. Return the path where to store the cache
  379. :param settings: The YAML settings file
  380. :type settings: dict
  381. :return: the path to store the cache
  382. :rtype: String
  383. :Example:
  384. cache_path = cls._get_cache_path(settings)
  385. .. seealso::
  386. .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
  387. .. warnings:: Class Method and Private
  388. """
  389. try:
  390. cache_path = settings["cache_path"]
  391. except KeyError, e:
  392. raise SettingNotFound("%s setting not found" % e)
  393. if cache_path is None:
  394. raise NullSettingException("cache_path setting cannot be null")
  395. # test if that path is usable
  396. if FileManager.is_path_exists_or_creatable(cache_path):
  397. return cache_path
  398. else:
  399. raise SettingInvalidException("The cache_path seems to be invalid: %s" % cache_path)
  400. @staticmethod
  401. def _get_default_synapse(settings):
  402. """
  403. Return the name of the default synapse
  404. :param settings: The YAML settings file
  405. :type settings: dict
  406. :return: the default synapse name
  407. :rtype: String
  408. :Example:
  409. default_synapse = cls._get_default_synapse(settings)
  410. .. seealso::
  411. .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
  412. .. warnings:: Class Method and Private
  413. """
  414. try:
  415. default_synapse = settings["default_synapse"]
  416. logger.debug("Default synapse: %s" % default_synapse)
  417. except KeyError:
  418. default_synapse = None
  419. return default_synapse
  420. @staticmethod
  421. def _get_resources(settings):
  422. """
  423. Return a resources object that contains path of third party modules
  424. :param settings: The YAML settings file
  425. :type settings: dict
  426. :return: the resource object
  427. :rtype: Resources
  428. :Example:
  429. resource_directory = cls._get_resource_dir(settings)
  430. .. seealso::
  431. .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
  432. .. warnings:: Class Method and Private
  433. """
  434. try:
  435. resource_dir = settings["resource_directory"]
  436. logger.debug("Resource directory synapse: %s" % resource_dir)
  437. neuron_folder = None
  438. stt_folder = None
  439. tts_folder = None
  440. trigger_folder = None
  441. if "neuron" in resource_dir:
  442. neuron_folder = resource_dir["neuron"]
  443. if not os.path.exists(neuron_folder):
  444. raise SettingInvalidException("The path %s does not exist on the system" % neuron_folder)
  445. if "stt" in resource_dir:
  446. stt_folder = resource_dir["stt"]
  447. if not os.path.exists(stt_folder):
  448. raise SettingInvalidException("The path %s does not exist on the system" % stt_folder)
  449. if "tts" in resource_dir:
  450. tts_folder = resource_dir["tts"]
  451. if not os.path.exists(tts_folder):
  452. raise SettingInvalidException("The path %s does not exist on the system" % tts_folder)
  453. if "trigger" in resource_dir:
  454. trigger_folder = resource_dir["trigger"]
  455. if not os.path.exists(trigger_folder):
  456. raise SettingInvalidException("The path %s does not exist on the system" % trigger_folder)
  457. if neuron_folder is None \
  458. and stt_folder is None \
  459. and tts_folder is None \
  460. and trigger_folder is None:
  461. raise SettingInvalidException("No required folder has been provided in the setting resource_directory. "
  462. "Define : \'neuron\' or/and \'stt\' or/and \'tts\' or/and \'trigger\'")
  463. resource_object = Resources(neuron_folder=neuron_folder,
  464. stt_folder=stt_folder,
  465. tts_folder=tts_folder,
  466. trigger_folder=trigger_folder)
  467. except KeyError:
  468. logger.debug("Resource directory not found in settings")
  469. resource_object = None
  470. return resource_object
  471. @staticmethod
  472. def _get_play_on_ready_notification(settings):
  473. """
  474. Return the on_ready_notification setting. If the user didn't provided it the default is never
  475. :param settings: The YAML settings file
  476. :type settings: dict
  477. :return:
  478. """
  479. try:
  480. play_on_ready_notification = settings["play_on_ready_notification"]
  481. except KeyError:
  482. # User does not provide this settings, by default we set it to never
  483. play_on_ready_notification = "never"
  484. return play_on_ready_notification
  485. return play_on_ready_notification
  486. @staticmethod
  487. def _get_on_ready_answers( settings):
  488. """
  489. Return the list of on_ready_answers string from the settings.
  490. :param settings: The YAML settings file
  491. :type settings: dict
  492. :return: String parameter on_ready_answers
  493. """
  494. try:
  495. on_ready_answers = settings["on_ready_answers"]
  496. except KeyError:
  497. # User does not provide this settings
  498. return None
  499. return on_ready_answers
  500. @staticmethod
  501. def _get_on_ready_sounds(settings):
  502. """
  503. Return the list of on_ready_sounds string from the settings.
  504. :param settings: The YAML settings file
  505. :type settings: dict
  506. :return: String parameter on_ready_sounds
  507. """
  508. try:
  509. on_ready_sounds = settings["on_ready_sounds"]
  510. # In case files are declared in settings.yml, make sure kalliope can access them.
  511. for sound in on_ready_sounds:
  512. if Utils.get_real_file_path(sound) is None:
  513. raise SettingInvalidException("sound file %s not found" % sound)
  514. except KeyError:
  515. # User does not provide this settings
  516. return None
  517. return on_ready_sounds