2017-10-04 06:28:44 +00:00
|
|
|
# Copyright 2017 Mycroft AI Inc.
|
2017-04-13 05:26:45 +00:00
|
|
|
#
|
2017-10-04 06:28:44 +00:00
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
2017-04-13 05:26:45 +00:00
|
|
|
#
|
2017-10-04 06:28:44 +00:00
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
2017-04-13 05:26:45 +00:00
|
|
|
#
|
2017-10-04 06:28:44 +00:00
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
2017-04-13 05:26:45 +00:00
|
|
|
#
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
2017-11-09 12:24:42 +00:00
|
|
|
SkillSettings is a simple extension of the python dict which enables
|
2017-11-09 10:34:00 +00:00
|
|
|
local storage of settings. Additionally it can interact with a backend
|
|
|
|
system to provide a GUI interface, described by meta-data described in
|
|
|
|
an optional 'settingsmeta.json' file.
|
2017-04-24 10:09:23 +00:00
|
|
|
|
2017-11-09 10:34:00 +00:00
|
|
|
Usage Example:
|
2017-04-24 10:09:23 +00:00
|
|
|
from mycroft.skill.settings import SkillSettings
|
|
|
|
|
2017-11-09 12:43:04 +00:00
|
|
|
s = SkillSettings('./settings.json', 'ImportantSettings')
|
2017-04-24 10:09:23 +00:00
|
|
|
s['meaning of life'] = 42
|
|
|
|
s['flower pot sayings'] = 'Not again...'
|
|
|
|
s.store()
|
2017-11-09 12:24:42 +00:00
|
|
|
|
2017-11-09 10:34:00 +00:00
|
|
|
Metadata format:
|
2017-11-09 12:24:42 +00:00
|
|
|
TODO: see https://goo.gl/MY3i1S
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
|
|
|
|
2017-04-13 05:26:45 +00:00
|
|
|
import json
|
2017-12-07 15:53:49 +00:00
|
|
|
import subprocess
|
|
|
|
import hashlib
|
2017-06-30 22:36:04 +00:00
|
|
|
from threading import Timer
|
2017-12-07 15:53:49 +00:00
|
|
|
from os.path import isfile, join, expanduser, basename
|
2017-09-18 19:14:21 +00:00
|
|
|
|
2017-06-28 16:32:19 +00:00
|
|
|
from mycroft.api import DeviceApi
|
2017-09-18 19:14:21 +00:00
|
|
|
from mycroft.util.log import LOG
|
2017-10-12 17:35:50 +00:00
|
|
|
from mycroft.configuration import ConfigurationManager
|
2017-06-28 16:32:19 +00:00
|
|
|
|
2017-10-04 06:28:44 +00:00
|
|
|
|
2017-04-13 05:26:45 +00:00
|
|
|
class SkillSettings(dict):
|
2017-11-09 10:34:00 +00:00
|
|
|
""" A dictionary that can easily be save to a file, serialized as json. It
|
|
|
|
also syncs to the backend for skill settings
|
2017-04-24 10:09:23 +00:00
|
|
|
|
|
|
|
Args:
|
2017-11-09 12:43:04 +00:00
|
|
|
directory (str): Path to storage directory
|
|
|
|
name (str): user readable name associated with the settings
|
2017-04-24 10:09:23 +00:00
|
|
|
"""
|
2017-09-18 19:14:21 +00:00
|
|
|
|
2017-10-12 17:35:50 +00:00
|
|
|
def __init__(self, directory, name):
|
2017-04-13 05:26:45 +00:00
|
|
|
super(SkillSettings, self).__init__()
|
2017-06-28 16:32:19 +00:00
|
|
|
self.api = DeviceApi()
|
|
|
|
self._device_identity = self.api.identity.uuid
|
2017-10-12 17:35:50 +00:00
|
|
|
self.config = ConfigurationManager.get()
|
|
|
|
self.name = name
|
2017-12-07 15:53:49 +00:00
|
|
|
self.directory = directory
|
2017-07-27 21:28:32 +00:00
|
|
|
# set file paths
|
2017-06-28 16:32:19 +00:00
|
|
|
self._settings_path = join(directory, 'settings.json')
|
|
|
|
self._meta_path = join(directory, 'settingsmeta.json')
|
|
|
|
self._api_path = "/" + self._device_identity + "/skill"
|
2017-10-13 21:06:07 +00:00
|
|
|
self.is_alive = True
|
2017-07-27 21:28:32 +00:00
|
|
|
self.loaded_hash = hash(str(self))
|
2017-06-28 22:31:35 +00:00
|
|
|
|
2017-07-27 21:28:32 +00:00
|
|
|
# if settingsmeta.json exists
|
2017-10-12 17:35:50 +00:00
|
|
|
# this block of code is a control flow for
|
|
|
|
# different scenarios that may arises with settingsmeta
|
2017-07-27 21:28:32 +00:00
|
|
|
if isfile(self._meta_path):
|
2017-10-12 17:35:50 +00:00
|
|
|
LOG.info("settingsmeta.json exist for {}".format(self.name))
|
|
|
|
settings_meta = self._load_settings_meta()
|
2017-12-07 15:53:49 +00:00
|
|
|
hashed_meta = self._get_meta_hash(str(settings_meta))
|
2017-12-07 20:53:17 +00:00
|
|
|
skill_settings = self._request_other_settings(hashed_meta)
|
2017-12-07 15:53:49 +00:00
|
|
|
# if hash is new then there is a diff version of settingsmeta
|
2017-10-12 17:35:50 +00:00
|
|
|
if self._is_new_hash(hashed_meta):
|
2017-12-07 15:53:49 +00:00
|
|
|
# first look at all other devices on user account to see
|
|
|
|
# if the settings exist. if it does then sync with this device
|
|
|
|
if skill_settings is not None:
|
2017-12-07 19:02:23 +00:00
|
|
|
# is_synced flags that this settings is loaded from
|
|
|
|
# another device. If a skill settings doesn't have
|
2017-12-07 15:53:49 +00:00
|
|
|
# is_synced, then the skill is created from that device
|
2017-12-07 20:53:17 +00:00
|
|
|
self['is_synced'] = True
|
2017-12-07 15:53:49 +00:00
|
|
|
self.save_skill_settings(skill_settings)
|
|
|
|
else: # upload skill settings if other devices do not have it
|
|
|
|
uuid = self._load_uuid()
|
|
|
|
if uuid is not None:
|
|
|
|
self._delete_metadata(uuid)
|
|
|
|
self._upload_meta(settings_meta, hashed_meta)
|
|
|
|
else: # hash is not new
|
|
|
|
if skill_settings is not None:
|
2017-12-07 20:53:17 +00:00
|
|
|
self['is_synced'] = True
|
2017-12-07 15:53:49 +00:00
|
|
|
self.save_skill_settings(skill_settings)
|
|
|
|
else:
|
2017-12-07 20:53:17 +00:00
|
|
|
settings = self._request_my_settings(hashed_meta)
|
2017-12-07 15:53:49 +00:00
|
|
|
if settings is None:
|
|
|
|
LOG.info("seems like it got deleted from home... "
|
|
|
|
"sending settingsmeta.json for "
|
|
|
|
"{}".format(self.name))
|
|
|
|
self._upload_meta(settings_meta, hashed_meta)
|
|
|
|
else:
|
|
|
|
self.save_skill_settings(settings)
|
2017-10-12 17:35:50 +00:00
|
|
|
|
2017-10-13 18:43:55 +00:00
|
|
|
t = Timer(60, self._poll_skill_settings, [hashed_meta])
|
|
|
|
t.daemon = True
|
|
|
|
t.start()
|
2017-07-27 21:38:24 +00:00
|
|
|
|
2017-07-27 21:28:32 +00:00
|
|
|
self.load_skill_settings()
|
2017-04-24 10:08:47 +00:00
|
|
|
|
2017-08-01 18:52:51 +00:00
|
|
|
@property
|
|
|
|
def _is_stored(self):
|
|
|
|
return hash(str(self)) == self.loaded_hash
|
|
|
|
|
2017-04-13 05:26:45 +00:00
|
|
|
def __getitem__(self, key):
|
2017-10-12 19:36:23 +00:00
|
|
|
""" Get key """
|
2017-04-13 05:26:45 +00:00
|
|
|
return super(SkillSettings, self).__getitem__(key)
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
2017-10-12 19:36:23 +00:00
|
|
|
""" Add/Update key. """
|
2017-04-13 05:26:45 +00:00
|
|
|
return super(SkillSettings, self).__setitem__(key, value)
|
|
|
|
|
2017-07-27 21:28:32 +00:00
|
|
|
def _load_settings_meta(self):
|
2017-11-09 12:43:04 +00:00
|
|
|
""" Loads settings metadata from skills path. """
|
2017-07-27 21:28:32 +00:00
|
|
|
with open(self._meta_path) as f:
|
|
|
|
data = json.load(f)
|
|
|
|
return data
|
|
|
|
|
2017-12-07 15:53:49 +00:00
|
|
|
def _send_settings_meta(self, settings_meta):
|
2017-12-07 19:02:23 +00:00
|
|
|
""" Send settingsmeta.json to the server.
|
2017-07-06 17:40:21 +00:00
|
|
|
|
2017-10-12 17:35:50 +00:00
|
|
|
Args:
|
2017-11-09 12:43:04 +00:00
|
|
|
settings_meta (dict): dictionary of the current settings meta
|
2017-10-12 17:35:50 +00:00
|
|
|
|
|
|
|
Returns:
|
2017-11-09 12:43:04 +00:00
|
|
|
str: uuid, a unique id for the setting meta data
|
2017-07-27 21:38:24 +00:00
|
|
|
"""
|
2017-07-11 23:19:21 +00:00
|
|
|
try:
|
2017-12-07 15:53:49 +00:00
|
|
|
uuid = self._put_metadata(settings_meta)
|
2017-10-12 17:35:50 +00:00
|
|
|
return uuid
|
2017-07-11 23:19:21 +00:00
|
|
|
except Exception as e:
|
2017-09-18 18:55:58 +00:00
|
|
|
LOG.error(e)
|
2017-12-07 15:53:49 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
def save_skill_settings(self, skill_settings):
|
|
|
|
""" takes skill object and save onto self
|
|
|
|
|
|
|
|
Args:
|
|
|
|
settings (dict): skill
|
|
|
|
"""
|
|
|
|
if self._is_new_hash(skill_settings['identifier']):
|
|
|
|
self._save_uuid(skill_settings['uuid'])
|
|
|
|
self._save_hash(skill_settings['identifier'])
|
|
|
|
sections = skill_settings['skillMetadata']['sections']
|
|
|
|
for section in sections:
|
|
|
|
for field in section["fields"]:
|
|
|
|
if "name" in field: # no name for 'label' fields
|
2017-12-07 20:53:17 +00:00
|
|
|
self[field['name']] = field['value']
|
2017-12-07 15:53:49 +00:00
|
|
|
self.store()
|
2017-06-28 22:31:35 +00:00
|
|
|
|
2017-10-12 17:35:50 +00:00
|
|
|
def _load_uuid(self):
|
2017-11-09 12:43:04 +00:00
|
|
|
""" Loads uuid
|
2017-10-12 17:35:50 +00:00
|
|
|
|
|
|
|
Returns:
|
2017-11-09 12:43:04 +00:00
|
|
|
str: uuid of the previous settingsmeta
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
2017-10-12 17:35:50 +00:00
|
|
|
directory = self.config.get("skills")["directory"]
|
|
|
|
directory = join(directory, self.name)
|
|
|
|
directory = expanduser(directory)
|
|
|
|
uuid_file = join(directory, 'uuid')
|
2017-12-07 15:53:49 +00:00
|
|
|
uuid = None
|
2017-10-12 17:35:50 +00:00
|
|
|
if isfile(uuid_file):
|
|
|
|
with open(uuid_file, 'r') as f:
|
|
|
|
uuid = f.read()
|
|
|
|
return uuid
|
|
|
|
|
|
|
|
def _save_uuid(self, uuid):
|
2017-12-07 19:02:23 +00:00
|
|
|
""" Saves uuid.
|
2017-10-12 17:35:50 +00:00
|
|
|
|
|
|
|
Args:
|
2017-11-09 12:43:04 +00:00
|
|
|
str: uuid, unique id of new settingsmeta
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
2017-10-12 17:35:50 +00:00
|
|
|
LOG.info("saving uuid {}".format(str(uuid)))
|
|
|
|
directory = self.config.get("skills")["directory"]
|
|
|
|
directory = join(directory, self.name)
|
|
|
|
directory = expanduser(directory)
|
|
|
|
uuid_file = join(directory, 'uuid')
|
|
|
|
with open(uuid_file, 'w') as f:
|
|
|
|
f.write(str(uuid))
|
|
|
|
|
2017-12-07 17:00:04 +00:00
|
|
|
def _uuid_exist(self):
|
|
|
|
""" Checks if there is an uuid file.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if uuid file exist False otherwise
|
|
|
|
"""
|
|
|
|
directory = self.config.get("skills")["directory"]
|
|
|
|
directory = join(directory, self.name)
|
|
|
|
directory = expanduser(directory)
|
|
|
|
uuid_file = join(directory, 'uuid')
|
|
|
|
return isfile(uuid_file)
|
|
|
|
|
2017-12-07 15:53:49 +00:00
|
|
|
def _migrate_settings(self, settings_meta):
|
2017-12-07 18:56:21 +00:00
|
|
|
""" sync settings.json and settingsmeta.json in memory """
|
2017-12-07 15:53:49 +00:00
|
|
|
meta = settings_meta.copy()
|
|
|
|
sections = meta['skillMetadata']['sections']
|
|
|
|
for i, section in enumerate(sections):
|
|
|
|
for j, field in enumerate(section['fields']):
|
|
|
|
if 'name' in field:
|
|
|
|
if field["name"] in self:
|
2017-12-07 20:53:17 +00:00
|
|
|
sections[i]['fields'][j]["value"] = self['name']
|
2017-12-07 15:53:49 +00:00
|
|
|
meta['skillMetadata']['sections'] = sections
|
|
|
|
return meta
|
|
|
|
|
|
|
|
def _upload_meta(self, settings_meta, hashed_meta):
|
|
|
|
""" uploads the new meta data to settings with settings migration
|
2017-10-12 17:35:50 +00:00
|
|
|
|
|
|
|
Args:
|
2017-12-07 15:53:49 +00:00
|
|
|
settings_meta (dict): settingsmeta.json
|
|
|
|
hashed_meta (str): {skill-folder}-settinsmeta.json
|
2017-07-11 20:25:43 +00:00
|
|
|
"""
|
2017-12-07 15:53:49 +00:00
|
|
|
LOG.info("sending settingsmeta.json for {}".format(self.name) +
|
2017-12-07 18:48:21 +00:00
|
|
|
" to servers")
|
2017-12-07 15:53:49 +00:00
|
|
|
meta = self._migrate_settings(settings_meta)
|
|
|
|
meta['identifier'] = str(hashed_meta)
|
2017-12-07 17:00:04 +00:00
|
|
|
response = self._send_settings_meta(meta)
|
|
|
|
self._save_uuid(response['uuid'])
|
2017-12-07 15:53:49 +00:00
|
|
|
self._save_hash(hashed_meta)
|
|
|
|
|
|
|
|
def _delete_old_meta(self):
|
|
|
|
"""" Deletes the old meta data """
|
|
|
|
if self._uuid_exist():
|
|
|
|
try:
|
|
|
|
LOG.info("a uuid exist for {}".format(self.name) +
|
|
|
|
" deleting old one")
|
|
|
|
old_uuid = self._load_uuid()
|
|
|
|
self._delete_metatdata(old_uuid)
|
|
|
|
except Exception as e:
|
|
|
|
LOG.info(e)
|
2017-10-12 17:35:50 +00:00
|
|
|
|
2017-12-07 15:53:49 +00:00
|
|
|
def hash(self, str):
|
|
|
|
""" md5 hasher for consistency across cpu architectures """
|
|
|
|
return hashlib.md5(str).hexdigest()
|
|
|
|
|
|
|
|
def _get_meta_hash(self, settings_meta):
|
|
|
|
""" Get's the hash of skill
|
|
|
|
|
|
|
|
Args:
|
|
|
|
settings_meta (str): stringified settingsmeta
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
_hash (str): hashed to identify skills
|
|
|
|
"""
|
|
|
|
return "{}-{}".format(
|
|
|
|
basename(self.directory),
|
|
|
|
self.hash(str(settings_meta)))
|
|
|
|
|
|
|
|
def _save_hash(self, hashed_meta):
|
|
|
|
""" Saves hashed_meta to settings directory.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
hashed_meta (int): hash of new settingsmeta
|
|
|
|
"""
|
|
|
|
LOG.info("saving hash {}".format(str(hashed_meta)))
|
|
|
|
directory = self.config.get("skills")["directory"]
|
|
|
|
directory = join(directory, self.name)
|
|
|
|
directory = expanduser(directory)
|
|
|
|
hash_file = join(directory, 'hash')
|
|
|
|
with open(hash_file, 'w') as f:
|
|
|
|
f.write(str(hashed_meta))
|
|
|
|
|
2017-10-12 17:35:50 +00:00
|
|
|
def _is_new_hash(self, hashed_meta):
|
|
|
|
""" checks if the stored hash is the same as current.
|
|
|
|
if the hashed file does not exist, usually in the
|
|
|
|
case of first load, then the create it and return True
|
|
|
|
|
|
|
|
Args:
|
2017-11-09 12:43:04 +00:00
|
|
|
hashed_meta (int): hash of metadata and uuid of device
|
2017-10-12 17:35:50 +00:00
|
|
|
|
|
|
|
Returns:
|
2017-11-09 12:43:04 +00:00
|
|
|
bool: True if hash is new, otherwise False
|
2017-10-12 17:35:50 +00:00
|
|
|
"""
|
|
|
|
directory = self.config.get("skills")["directory"]
|
|
|
|
directory = join(directory, self.name)
|
|
|
|
directory = expanduser(directory)
|
|
|
|
hash_file = join(directory, 'hash')
|
|
|
|
if isfile(hash_file):
|
|
|
|
with open(hash_file, 'r') as f:
|
|
|
|
current_hash = f.read()
|
|
|
|
return False if current_hash == str(hashed_meta) else True
|
|
|
|
return True
|
|
|
|
|
2017-12-07 20:53:17 +00:00
|
|
|
def update(self):
|
|
|
|
""" update settings state from server """
|
|
|
|
skills_settings = None
|
|
|
|
if self.get('is_synced'):
|
|
|
|
skills_settings = self._request_other_settings(hashed_meta)
|
|
|
|
if not skills_settings:
|
|
|
|
skills_settings = self._request_my_settings(hashed_meta)
|
|
|
|
|
|
|
|
if skills_settings is not None:
|
|
|
|
self.save_skill_settings(skills_settings)
|
|
|
|
self.store()
|
|
|
|
|
2017-10-12 17:35:50 +00:00
|
|
|
def _poll_skill_settings(self, hashed_meta):
|
|
|
|
""" If identifier exists for this skill poll to backend to
|
|
|
|
request settings and store it if it changes
|
|
|
|
TODO: implement as websocket
|
|
|
|
|
|
|
|
Args:
|
2017-11-09 12:43:04 +00:00
|
|
|
hashed_meta (int): the hashed identifier
|
2017-10-12 17:35:50 +00:00
|
|
|
"""
|
2017-12-07 20:53:17 +00:00
|
|
|
skills_settings = None
|
|
|
|
if self.get('is_synced'):
|
|
|
|
skills_settings = self._request_other_settings(hashed_meta)
|
|
|
|
if not skills_settings:
|
|
|
|
skills_settings = self._request_my_settings(hashed_meta)
|
2017-12-07 15:53:49 +00:00
|
|
|
|
|
|
|
if skills_settings is not None:
|
|
|
|
self.save_skill_settings(skills_settings)
|
|
|
|
self.store()
|
|
|
|
else:
|
|
|
|
settings_meta = self._load_settings_meta()
|
|
|
|
self._upload_meta(settings_meta, hashed_meta)
|
2017-10-12 17:35:50 +00:00
|
|
|
|
2017-10-13 21:06:07 +00:00
|
|
|
if self.is_alive:
|
|
|
|
# continues to poll settings every 60 seconds
|
|
|
|
t = Timer(60, self._poll_skill_settings, [hashed_meta])
|
|
|
|
t.daemon = True
|
|
|
|
t.start()
|
2017-06-28 22:31:35 +00:00
|
|
|
|
2017-07-27 21:28:32 +00:00
|
|
|
def load_skill_settings(self):
|
2017-10-12 19:36:23 +00:00
|
|
|
""" If settings.json exist, open and read stored values into self """
|
2017-06-28 22:31:35 +00:00
|
|
|
if isfile(self._settings_path):
|
|
|
|
with open(self._settings_path) as f:
|
|
|
|
try:
|
|
|
|
json_data = json.load(f)
|
|
|
|
for key in json_data:
|
2017-12-07 20:53:17 +00:00
|
|
|
self[key] = json_data[key]
|
2017-06-28 22:31:35 +00:00
|
|
|
except Exception as e:
|
|
|
|
# TODO: Show error on webUI. Dev will have to fix
|
|
|
|
# metadata to be able to edit later.
|
2017-09-18 18:55:58 +00:00
|
|
|
LOG.error(e)
|
2017-06-28 16:32:19 +00:00
|
|
|
|
2017-12-07 20:53:17 +00:00
|
|
|
def _request_my_settings(self, identifier):
|
2017-12-07 15:53:49 +00:00
|
|
|
""" Get skill settings for this device associated
|
|
|
|
with the identifier
|
|
|
|
|
|
|
|
Args:
|
|
|
|
identifier (str): a hashed_meta
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
skill_settings (dict or None): returns a dict if matches
|
|
|
|
"""
|
2017-12-07 20:53:17 +00:00
|
|
|
LOG.info("getting skill settings from "
|
|
|
|
"server for {}".format(self.name))
|
|
|
|
settings = self._request_settings()
|
2017-12-07 15:53:49 +00:00
|
|
|
# this loads the settings into memory for use in self.store
|
|
|
|
for skill_settings in settings:
|
|
|
|
if skill_settings['identifier'] == identifier:
|
|
|
|
self._remote_settings = skill_settings
|
|
|
|
return skill_settings
|
|
|
|
return None
|
|
|
|
|
2017-12-07 20:53:17 +00:00
|
|
|
def _request_settings(self):
|
2017-12-07 19:02:23 +00:00
|
|
|
""" Get all skill settings for this device from server.
|
2017-11-09 12:43:04 +00:00
|
|
|
|
|
|
|
Returns:
|
2017-12-07 19:02:23 +00:00
|
|
|
dict: dictionary with settings collected from the server.
|
2017-11-09 12:43:04 +00:00
|
|
|
"""
|
2017-10-13 22:34:22 +00:00
|
|
|
settings = self.api.request({
|
2017-06-28 16:32:19 +00:00
|
|
|
"method": "GET",
|
|
|
|
"path": self._api_path
|
|
|
|
})
|
2017-10-13 22:34:22 +00:00
|
|
|
settings = [skills for skills in settings if skills is not None]
|
|
|
|
return settings
|
2017-06-28 16:32:19 +00:00
|
|
|
|
2017-12-07 20:53:17 +00:00
|
|
|
def _request_other_settings(self, identifier):
|
|
|
|
""" Retrieves user skill from other devices by identifier (hashed_meta)
|
2017-12-07 15:53:49 +00:00
|
|
|
|
|
|
|
Args:
|
|
|
|
indentifier (str): identifier for this skill
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
settings (dict or None): returns the settings if true else None
|
|
|
|
"""
|
2017-12-07 20:53:17 +00:00
|
|
|
LOG.info(
|
|
|
|
"syncing settings with other devices "
|
|
|
|
"from server for {}".format(self.name))
|
|
|
|
|
2017-12-07 15:53:49 +00:00
|
|
|
path = \
|
|
|
|
"/" + self._device_identity + "/userSkill?identifier=" + identifier
|
|
|
|
user_skill = self.api.request({
|
|
|
|
"method": "GET",
|
|
|
|
"path": path
|
|
|
|
})
|
|
|
|
if len(user_skill) == 0:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return user_skill[0]
|
|
|
|
|
2017-07-11 20:25:43 +00:00
|
|
|
def _put_metadata(self, settings_meta):
|
2017-12-07 18:48:21 +00:00
|
|
|
""" PUT settingsmeta to backend to be configured in server.
|
2017-11-09 12:43:04 +00:00
|
|
|
used in place of POST and PATCH.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
settings_meta (dict): dictionary of the current settings meta
|
|
|
|
data
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
2017-06-28 16:32:19 +00:00
|
|
|
return self.api.request({
|
2017-07-11 20:25:43 +00:00
|
|
|
"method": "PUT",
|
2017-06-28 16:32:19 +00:00
|
|
|
"path": self._api_path,
|
|
|
|
"json": settings_meta
|
|
|
|
})
|
|
|
|
|
2017-12-07 15:53:49 +00:00
|
|
|
def _delete_metadata(self, uuid):
|
2017-10-12 17:35:50 +00:00
|
|
|
""" Deletes the current skill metadata
|
|
|
|
|
|
|
|
Args:
|
2017-11-09 12:43:04 +00:00
|
|
|
uuid (str): unique id of the skill
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
2017-12-07 15:53:49 +00:00
|
|
|
try:
|
|
|
|
LOG.info("deleting metadata")
|
|
|
|
self.api.request({
|
|
|
|
"method": "DELETE",
|
|
|
|
"path": self._api_path + "/{}".format(uuid)
|
|
|
|
})
|
|
|
|
except Exception as e:
|
|
|
|
LOG.error(e)
|
|
|
|
LOG.info(
|
|
|
|
"cannot delete metadata because this"
|
|
|
|
"device is not original uploader of skill")
|
2017-10-12 17:35:50 +00:00
|
|
|
|
2017-12-07 18:36:01 +00:00
|
|
|
@property
|
|
|
|
def _should_upload_from_change(self):
|
|
|
|
changed = False
|
|
|
|
if hasattr(self, '_remote_settings'):
|
|
|
|
sections = self._remote_settings['skillMetadata']['sections']
|
|
|
|
for i, section in enumerate(sections):
|
|
|
|
for j, field in enumerate(section['fields']):
|
|
|
|
if 'name' in field:
|
|
|
|
if field["name"] in self:
|
|
|
|
remote_val = sections[i]['fields'][j]["value"]
|
2017-12-07 20:53:17 +00:00
|
|
|
self_val = self.get(field['name'])
|
2017-12-07 18:36:01 +00:00
|
|
|
if str(remote_val) != str(self_val):
|
|
|
|
changed = True
|
|
|
|
return changed
|
|
|
|
|
2017-10-12 17:35:50 +00:00
|
|
|
def store(self, force=False):
|
|
|
|
""" Store dictionary to file if a change has occured.
|
2017-09-01 08:07:49 +00:00
|
|
|
|
|
|
|
Args:
|
|
|
|
force: Force write despite no change
|
2017-06-28 22:31:35 +00:00
|
|
|
"""
|
2017-12-07 18:36:01 +00:00
|
|
|
|
2017-09-01 08:07:49 +00:00
|
|
|
if force or not self._is_stored:
|
|
|
|
with open(self._settings_path, 'w') as f:
|
|
|
|
json.dump(self, f)
|
|
|
|
self.loaded_hash = hash(str(self))
|
2017-12-07 18:36:01 +00:00
|
|
|
|
|
|
|
if self._should_upload_from_change:
|
|
|
|
settings_meta = self._load_settings_meta()
|
|
|
|
hashed_meta = self._get_meta_hash(settings_meta)
|
|
|
|
uuid = self._load_uuid()
|
|
|
|
if uuid is not None:
|
|
|
|
LOG.info("deleting meata data for {}".format(self.name))
|
|
|
|
self._delete_metadata(uuid)
|
|
|
|
self._upload_meta(settings_meta, hashed_meta)
|