SettingLoader.py 22 KB

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