Skip to content

Commit

Permalink
Merge pull request #296 from OpenVoiceOS/release-3.0.0a1
Browse files Browse the repository at this point in the history
Release 3.0.0a1
  • Loading branch information
JarbasAl authored Nov 19, 2024
2 parents c92dae6 + 4b8e4dd commit b6ac15d
Show file tree
Hide file tree
Showing 11 changed files with 14 additions and 276 deletions.
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Changelog

## [2.4.4a1](https://github.com/OpenVoiceOS/OVOS-workshop/tree/2.4.4a1) (2024-11-16)
## [3.0.0a1](https://github.com/OpenVoiceOS/OVOS-workshop/tree/3.0.0a1) (2024-11-19)

[Full Changelog](https://github.com/OpenVoiceOS/OVOS-workshop/compare/2.4.3...2.4.4a1)
[Full Changelog](https://github.com/OpenVoiceOS/OVOS-workshop/compare/2.4.4...3.0.0a1)

**Breaking changes:**

- deprecate!:backend client [\#295](https://github.com/OpenVoiceOS/OVOS-workshop/pull/295) ([JarbasAl](https://github.com/JarbasAl))

**Merged pull requests:**

- nl-nl translation [\#292](https://github.com/OpenVoiceOS/OVOS-workshop/pull/292) ([gitlocalize-app[bot]](https://github.com/apps/gitlocalize-app))
- Create skill.error.dialog nl-nl [\#294](https://github.com/OpenVoiceOS/OVOS-workshop/pull/294) ([timonvanhasselt](https://github.com/timonvanhasselt))



Expand Down
16 changes: 1 addition & 15 deletions ovos_workshop/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,15 @@
class OVOSAbstractApplication(OVOSSkill):
def __init__(self, skill_id: str, bus: Optional[MessageBusClient] = None,
resources_dir: Optional[str] = None,
lang=None, settings: Optional[dict] = None,
gui: Optional[GUIInterface] = None,
enable_settings_manager: bool = False, **kwargs):
gui: Optional[GUIInterface] = None, **kwargs):
"""
Create an Application. An application is essentially a skill, but
designed such that it may be run without an intent service.
@param skill_id: Unique ID for this application
@param bus: MessageBusClient to bind to application
@param resources_dir: optional root resource directory (else defaults to
application `root_dir`
@param lang: DEPRECATED language of the application
@param settings: DEPRECATED settings object
@param gui: GUIInterface to bind (if `None`, one is created)
@param enable_settings_manager: if True, enables a SettingsManager for
this application to manage default settings and backend sync
"""
self._dedicated_bus = False
if bus:
Expand All @@ -38,15 +32,7 @@ def __init__(self, skill_id: str, bus: Optional[MessageBusClient] = None,

super().__init__(skill_id=skill_id, bus=bus, gui=gui,
resources_dir=resources_dir,
enable_settings_manager=enable_settings_manager,
**kwargs)

if settings:
log_deprecation(f"Settings should be set in {self.settings_path}. "
f"Passing `settings` to __init__ is not supported.",
"0.1.0")
self.settings.merge(settings)

@property
def settings_path(self) -> str:
"""
Expand Down
1 change: 1 addition & 0 deletions ovos_workshop/res/text/nl/skill.error.dialog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Er is een fout opgetreden in {skill}
162 changes: 0 additions & 162 deletions ovos_workshop/settings.py
Original file line number Diff line number Diff line change
@@ -1,167 +1,5 @@
import json
from os.path import isfile
from threading import Timer
from typing import Optional

import yaml
from json_database import JsonStorageXDG

from ovos_backend_client.api import DeviceApi
from ovos_backend_client.pairing import is_paired, requires_backend
from ovos_backend_client.settings import RemoteSkillSettings, get_display_name
from ovos_bus_client import MessageBusClient
from ovos_bus_client.message import Message, dig_for_message
from ovos_utils.log import LOG


class SkillSettingsManager:
def __init__(self, skill):
self.download_timer: Optional[Timer] = None
self.skill = skill
self.api = DeviceApi()
self.remote_settings = \
RemoteSkillSettings(self.skill_id,
settings=dict(self.skill.settings),
meta=self.load_meta(), remote_id=self.skill_gid)
self.register_bus_handlers()

def start(self):
self._download()

def _download(self):
# If this method is called outside of the timer loop, ensure the
# existing timer is canceled before starting a new one.
if self.download_timer:
self.download_timer.cancel()

self.download()

# prepare to download again in 60 seconds
self.download_timer = Timer(60, self._download)
self.download_timer.daemon = True
self.download_timer.start()

def stop(self):
# If this method is called outside of the timer loop, ensure the
# existing timer is canceled
if self.download_timer:
self.download_timer.cancel()

@property
def bus(self) -> MessageBusClient:
return self.skill.bus

@property
def skill_id(self) -> str:
return self.skill.skill_id

@property
def display_name(self) -> str:
return get_display_name(self.skill_id)

@property
def skill_gid(self) -> str:
return f"@{self.api.uuid}|{self.skill_id}"

@property
def skill_meta(self) -> dict:
return self.remote_settings.meta

def register_bus_handlers(self):
self.skill.add_event('mycroft.skills.settings.update',
self.handle_download_remote) # backwards compat
self.skill.add_event('mycroft.skills.settings.download',
self.handle_download_remote)
self.skill.add_event('mycroft.skills.settings.upload',
self.handle_upload_local)
self.skill.add_event('mycroft.skills.settings.upload.meta',
self.handle_upload_meta)
self.skill.add_event('mycroft.paired',
self.handle_upload_local)

def load_meta(self) -> dict:
json_path = f"{self.skill.root_dir}/settingsmeta.json"
yaml_path = f"{self.skill.root_dir}/settingsmeta.yaml"
if isfile(yaml_path):
with open(yaml_path) as meta_file:
return yaml.safe_load(meta_file)
elif isfile(json_path):
with open(json_path) as meta_file:
return json.load(meta_file)
return {}

def save_meta(self, generate: bool = False):
# unset reload flag to avoid a reload on settingmeta change
# TODO - support for settingsmeta XDG paths
reload = self.skill.reload_skill
self.skill.reload_skill = False

# generate meta for missing fields
if generate:
self.remote_settings.generate_meta()

# write to disk
json_path = f"{self.skill.root_dir}/settingsmeta.json"
yaml_path = f"{self.skill.root_dir}/settingsmeta.yaml"
if isfile(yaml_path):
with open(yaml_path) as meta_file:
yaml.dump(self.remote_settings.meta, meta_file)
else:
with open(json_path, "w") as meta_file:
json.dump(self.remote_settings.meta, meta_file)

# reset reloading flag
self.skill.reload_skill = reload

@requires_backend
def upload(self, generate: bool = False):
if not is_paired():
LOG.debug("Device needs to be paired to upload settings")
return
self.remote_settings.settings = dict(self.skill.settings)
if generate:
self.remote_settings.generate_meta()
self.remote_settings.upload()

@requires_backend
def upload_meta(self, generate: bool = False):
if not is_paired():
LOG.debug("Device needs to be paired to upload settingsmeta")
return
if generate:
self.remote_settings.settings = dict(self.skill.settings)
self.remote_settings.generate_meta()
self.remote_settings.upload_meta()

@requires_backend
def download(self):
if not is_paired():
LOG.debug("Device needs to be paired to download remote settings")
return
self.remote_settings.download()
# we do not update skill object settings directly
# skill will handle the event and trigger a callback
if self.skill.settings != self.remote_settings.settings:
# dig old message to keep context
msg = dig_for_message() or Message("")
msg = msg.forward('mycroft.skills.settings.changed')

msg.data[self.skill_id] = self.remote_settings.settings
self.bus.emit(msg)

def handle_upload_meta(self, message: Message):
skill_id = message.data.get("skill_id")
if skill_id == self.skill_id:
self.upload_meta()

def handle_upload_local(self, message: Message):
skill_id = message.data.get("skill_id")
if skill_id == self.skill_id:
self.upload()

def handle_download_remote(self, message: Message):
self.download()


def settings2meta(settings, section_name="Skill Settings"):
""" generates basic settingsmeta """
Expand Down
32 changes: 0 additions & 32 deletions ovos_workshop/skills/ovos.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
from ovos_workshop.resource_files import ResourceFile, \
CoreResources, find_resource, SkillResources
from ovos_workshop.settings import PrivateSettings
from ovos_workshop.settings import SkillSettingsManager


def simple_trace(stack_trace: List[str]) -> str:
Expand Down Expand Up @@ -92,7 +91,6 @@ def __init__(self, name: Optional[str] = None,
resources_dir: Optional[str] = None,
settings: Optional[JsonStorage] = None,
gui: Optional[GUIInterface] = None,
enable_settings_manager: bool = True,
skill_id: str = ""):
"""
Create an OVOSSkill object.
Expand All @@ -103,18 +101,12 @@ def __init__(self, name: Optional[str] = None,
@param settings: Optional settings object, else defined in skill config
path
@param gui: Optional SkillGUI, else one is initialized
@param enable_settings_manager: if True, enables a SettingsManager for
this skill to manage default settings and backend sync
@param skill_id: Unique ID for this skill
"""

self.log = LOG # a dedicated namespace will be assigned in _startup
self._enable_settings_manager = enable_settings_manager
self._init_event = Event()
self.name = name or self.__class__.__name__
self.skill_id = skill_id # set by SkillLoader, guaranteed unique
self._settings_meta = None # DEPRECATED - backwards compat only
self.settings_manager = None
self.private_settings = None

# Get directory of skill source (__init__.py)
Expand Down Expand Up @@ -811,8 +803,6 @@ def _startup(self, bus: MessageBusClient, skill_id: str = ""):
self.status.set_alive()
if not self.gui:
self._init_skill_gui()
if self._enable_settings_manager:
self._init_settings_manager()
self.load_data_files()
self._register_skill_json()
self._register_decorated()
Expand Down Expand Up @@ -917,12 +907,6 @@ def _init_skill_gui(self):
self.gui = SkillGUI(self)
self.gui.setup_default_handlers()

def _init_settings_manager(self):
"""
Set up the SkillSettingsManager for this skill.
"""
self.settings_manager = SkillSettingsManager(self)

def register_homescreen_app(self, icon: str, name: str, event: str):
"""the icon file MUST be located under 'gui' subfolder"""
# this path is hardcoded in ovos_gui.constants and follows XDG spec
Expand Down Expand Up @@ -1025,19 +1009,6 @@ def _register_decorated(self):
for intent_file in getattr(method, 'converse_intents'):
self.register_converse_intent(intent_file, method)

def _upload_settings(self):
"""
Upload settings to a remote backend if configured.
"""
if self.settings_manager and self.config_core.get("skills", {}).get("sync2way"):
# upload new settings to backend
generate = self.config_core.get("skills", {}).get("autogen_meta", True)
# this will check global sync flag
self.settings_manager.upload(generate)
if generate:
# update settingsmeta file on disk
self.settings_manager.save_meta()

def bind(self, bus: MessageBusClient):
"""
Register MessageBusClient with skill.
Expand Down Expand Up @@ -1154,7 +1125,6 @@ def _handle_settings_file_change(self, path: str):
except Exception as e:
self.log.exception("settings change callback failed, "
f"file changes not handled!: {e}")
self._upload_settings()

def handle_settings_change(self, message: Message):
"""
Expand Down Expand Up @@ -1334,8 +1304,6 @@ def default_shutdown(self):
# Store settings
if self.settings != self._initial_settings:
self.settings.store()
if self._settings_meta:
self._settings_meta.stop()
if self._settings_watchdog:
self._settings_watchdog.shutdown()
except Exception as e:
Expand Down
8 changes: 4 additions & 4 deletions ovos_workshop/version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# START_VERSION_BLOCK
VERSION_MAJOR = 2
VERSION_MINOR = 4
VERSION_BUILD = 4
VERSION_ALPHA = 0
VERSION_MAJOR = 3
VERSION_MINOR = 0
VERSION_BUILD = 0
VERSION_ALPHA = 1
# END_VERSION_BLOCK
1 change: 0 additions & 1 deletion requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
ovos-utils>= 0.2.1,<1.0.0
ovos_bus_client>=0.0.8,<2.0.0
ovos-config>=0.0.12,<1.0.0
ovos-backend-client>=0.1.0,<2.0.0
ovos-solver-yes-no-plugin>=0.0.1,<1.0.0
ovos-number-parser>=0.0.1,<1.0.0
rapidfuzz
Expand Down
13 changes: 0 additions & 13 deletions test/unittests/skills/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def tearDownClass(cls) -> None:
shutil.rmtree(cls.test_config_path)

def test_00_skill_init(self):
from ovos_workshop.settings import SkillSettingsManager
from ovos_workshop.skills.ovos import SkillGUI
from ovos_utils.events import EventContainer, EventSchedulerInterface
from ovos_workshop.intents import IntentServiceInterface
Expand All @@ -36,10 +35,8 @@ def test_00_skill_init(self):
from ovos_workshop.resource_files import SkillResources

self.assertIsInstance(self.skill.log, Logger)
self.assertIsInstance(self.skill._enable_settings_manager, bool)
self.assertEqual(self.skill.name, self.skill.__class__.__name__)
self.assertEqual(self.skill.skill_id, self.skill_id)
self.assertIsInstance(self.skill.settings_manager, SkillSettingsManager)
self.assertTrue(isdir(self.skill.root_dir))
self.assertEqual(self.skill.res_dir, self.skill.root_dir)
self.assertIsInstance(self.skill.gui, SkillGUI)
Expand Down Expand Up @@ -170,28 +167,18 @@ def test_upload_settings(self):
pass

def test_handle_settings_file_change(self):
real_upload = self.skill._upload_settings
self.skill._upload_settings = Mock()
settings_file = self.skill.settings.path

# Handle change with no callback
self.skill._handle_settings_file_change(settings_file)
self.skill._upload_settings.assert_called_once()

# Handle change with callback
self.skill._upload_settings.reset_mock()
self.skill.settings_change_callback = Mock()
self.skill._handle_settings_file_change(settings_file)
self.skill._upload_settings.assert_called_once()
self.skill.settings_change_callback.assert_called_once()

# Handle non-settings file change
self.skill._handle_settings_file_change(join(dirname(settings_file),
"test.file"))
self.skill._upload_settings.assert_called_once()
self.skill.settings_change_callback.assert_called_once()

self.skill._upload_settings = real_upload

def test_load_lang(self):
# TODO
Expand Down
Loading

0 comments on commit b6ac15d

Please sign in to comment.