Creating access layer for the device and skill settings
- Replaced the function getcwd() for path.dirname(__file__) as a safer solution to get the current folder - Created functions to fetch devices using either id or account id - Created function get_setting_by_device_id_and_setting_version_hash to fetch a skill setting using the setting version hash and the device id The last function hits multiple tables and to avoid trigger a lot of queries we: - use a single query to fetch all sections for a given skill and device; - use a single query to fetch all settings passing the list of section ids from the previous step; - use a single query to fetch all skill settings for a given device and skillpull/30/head
parent
f24946ab30
commit
acb77b1566
|
@ -1,10 +1,9 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from decimal import Decimal
|
from os import path
|
||||||
from os import getcwd, path
|
|
||||||
|
|
||||||
from selene_util.db import DatabaseQuery, fetch
|
from selene_util.db import DatabaseQuery, fetch
|
||||||
|
|
||||||
SQL_DIR = path.join(getcwd(), 'sql')
|
SQL_DIR = path.join(path.dirname(__file__), 'sql')
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -12,16 +11,15 @@ class Account(object):
|
||||||
"""Representation of a Mycroft user account."""
|
"""Representation of a Mycroft user account."""
|
||||||
id: str
|
id: str
|
||||||
email_address: str
|
email_address: str
|
||||||
first_name: str
|
|
||||||
last_name: str
|
|
||||||
password: str
|
|
||||||
latitude: Decimal
|
|
||||||
longitude: Decimal
|
|
||||||
date_format: str
|
date_format: str
|
||||||
time_format: str
|
time_format: str
|
||||||
measurement_system: str
|
measurement_system: str
|
||||||
wake_word: str
|
wake_word: str
|
||||||
text_to_speech_id: str
|
text_to_speech_id: str
|
||||||
|
first_name: str = None
|
||||||
|
last_name: str = None
|
||||||
|
password: str = None
|
||||||
|
picture: str = None
|
||||||
|
|
||||||
|
|
||||||
def get_account_by_id(db, account_id: str) -> Account:
|
def get_account_by_id(db, account_id: str) -> Account:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .device import get_device_by_id
|
||||||
|
from .device import get_devices_by_account_id
|
|
@ -0,0 +1,54 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
from selene_util.db import DatabaseQuery, fetch
|
||||||
|
|
||||||
|
SQL_DIR = path.join(path.dirname(__file__), 'sql')
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Device(object):
|
||||||
|
"""Representation of a Device"""
|
||||||
|
id: str
|
||||||
|
account_id: str
|
||||||
|
name: str
|
||||||
|
platform: str = None
|
||||||
|
enclosure_version: str = None
|
||||||
|
core_version: str = None
|
||||||
|
category_id: str = None
|
||||||
|
location_id: str = None
|
||||||
|
placement: str = None
|
||||||
|
last_contact_ts: datetime = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_device_by_id(db, device_id: str) -> Device:
|
||||||
|
"""Fetch a device using a given device id
|
||||||
|
|
||||||
|
:param db: psycopg2 connection to mycroft database
|
||||||
|
:param device_id: uuid
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = DatabaseQuery(
|
||||||
|
file_path=path.join(SQL_DIR, 'get_device_by_id.sql'),
|
||||||
|
args=dict(device_id=device_id),
|
||||||
|
singleton=True
|
||||||
|
)
|
||||||
|
sql_result = fetch(db, query)
|
||||||
|
return Device(**sql_result)
|
||||||
|
|
||||||
|
|
||||||
|
def get_devices_by_account_id(db, account_id: str) -> list[Device]:
|
||||||
|
"""Fetch all devices associated to a user from a given account id
|
||||||
|
|
||||||
|
:param db: psycopg2 connection to mycroft database
|
||||||
|
:param account_id: uuid
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = DatabaseQuery(
|
||||||
|
file_path=path.join(SQL_DIR, 'get_devices_by_account_id.sql'),
|
||||||
|
args=dict(account_id=account_id),
|
||||||
|
singleton=False
|
||||||
|
)
|
||||||
|
sql_results = fetch(db, query)
|
||||||
|
return list(map(lambda result: Device(**result), sql_results))
|
|
@ -0,0 +1 @@
|
||||||
|
SELECT * FROM device.device WHERE id = %(device_id)s
|
|
@ -0,0 +1 @@
|
||||||
|
SELECT * FROM device.device WHERE account_id = %(account_id)s
|
|
@ -0,0 +1 @@
|
||||||
|
from .db import get_view_connection
|
|
@ -9,12 +9,15 @@ Example Usage:
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from os import path
|
from os import path
|
||||||
|
from os import environ
|
||||||
|
|
||||||
from psycopg2 import connect
|
from psycopg2 import connect
|
||||||
from psycopg2.extras import RealDictCursor
|
from psycopg2.extras import RealDictCursor
|
||||||
|
|
||||||
_log = getLogger(__package__)
|
_log = getLogger(__package__)
|
||||||
|
|
||||||
|
DB_HOST = environ['DB_HOST']
|
||||||
|
|
||||||
|
|
||||||
def get_sql_from_file(file_path: str) -> str:
|
def get_sql_from_file(file_path: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -39,6 +42,7 @@ def _connect_to_mycroft_db(db_user):
|
||||||
:return: database connection
|
:return: database connection
|
||||||
"""
|
"""
|
||||||
db = connect(
|
db = connect(
|
||||||
|
host=DB_HOST,
|
||||||
dbname='mycroft',
|
dbname='mycroft',
|
||||||
user=db_user,
|
user=db_user,
|
||||||
cursor_factory=RealDictCursor
|
cursor_factory=RealDictCursor
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .skill import get_setting_sections_by_device_id_and_setting_version
|
||||||
|
from .skill import get_setting_by_section_id
|
||||||
|
from .skill import get_setting_by_device_id_and_setting_version_hash
|
|
@ -0,0 +1,118 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
from selene_util.db import DatabaseQuery, fetch
|
||||||
|
|
||||||
|
SQL_DIR = path.join(path.dirname(__file__), 'sql')
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Setting(object):
|
||||||
|
"""Representation of a Skill setting"""
|
||||||
|
id: str
|
||||||
|
setting_section_id: str
|
||||||
|
setting: str
|
||||||
|
setting_type: str
|
||||||
|
hidden: bool
|
||||||
|
display_order: int
|
||||||
|
hint: str = None
|
||||||
|
label: str = None
|
||||||
|
placeholder: str = None
|
||||||
|
options: str = None
|
||||||
|
default_value: str = None
|
||||||
|
value: str = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SettingSection(object):
|
||||||
|
"""Representation of a section from a Skill Setting"""
|
||||||
|
id: str
|
||||||
|
skill_version_id: str
|
||||||
|
section: str
|
||||||
|
display_order: int
|
||||||
|
description: str = None
|
||||||
|
settings: list = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SkillSetting(object):
|
||||||
|
"""Representation of the link between a device and a skill setting """
|
||||||
|
id: str
|
||||||
|
device_skill_id: str
|
||||||
|
setting_id: str
|
||||||
|
value: str = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_setting_sections_by_device_id_and_setting_version(db, device_id, setting_version_hash):
|
||||||
|
""" Fetch all sections of a skill for a given device and setting version
|
||||||
|
|
||||||
|
:param db: psycopg2 connection to the mycroft database
|
||||||
|
:param device_id: uuid
|
||||||
|
:param setting_version_hash: version_hash for a given skill setting
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = DatabaseQuery(
|
||||||
|
file_path=path.join(SQL_DIR, 'get_setting_sections_by_device_id_and_setting_version.sql'),
|
||||||
|
args=dict(device_id=device_id, setting_version_hash=setting_version_hash),
|
||||||
|
singleton=False
|
||||||
|
)
|
||||||
|
sql_results = fetch(db, query)
|
||||||
|
return list(map(lambda result: SettingSection(**result), sql_results))
|
||||||
|
|
||||||
|
|
||||||
|
def get_setting_by_section_id(db, setting_section_ids):
|
||||||
|
""" Fetch all settings for a given list of section ids
|
||||||
|
|
||||||
|
:param db: psycopg2 connection to the mycroft database
|
||||||
|
:param setting_section_ids: list of section ids
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = DatabaseQuery(
|
||||||
|
file_path=path.join(SQL_DIR, 'get_setting_by_section_ids.sql'),
|
||||||
|
args=dict(setting_section_ids=setting_section_ids),
|
||||||
|
singleton=False
|
||||||
|
)
|
||||||
|
sql_results = fetch(db, query)
|
||||||
|
return list(map(lambda result: Setting(**result), sql_results))
|
||||||
|
|
||||||
|
|
||||||
|
def get_skill_settings_by_device_id_and_version_hash(db, device_id, setting_version_hash):
|
||||||
|
"""Fetch the skill settings for a given device and setting version hash
|
||||||
|
|
||||||
|
:param db: psycopg2 connection to the mycroft database
|
||||||
|
:param device_id: uuid
|
||||||
|
:param setting_version_hash:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = DatabaseQuery(
|
||||||
|
file_path=path.join(SQL_DIR, 'get_skill_setting_by_device_id_and_version_hash.sql'),
|
||||||
|
args=dict(device_id=device_id, setting_version_hash=setting_version_hash),
|
||||||
|
singleton=False
|
||||||
|
)
|
||||||
|
sql_results = fetch(db, query)
|
||||||
|
return list(map(lambda result: SkillSetting(**result), sql_results))
|
||||||
|
|
||||||
|
|
||||||
|
def get_setting_by_device_id_and_setting_version_hash(db, device_id, setting_version_hash):
|
||||||
|
"""Fetch the skill settings filling the setting using the value from the table skill setting
|
||||||
|
:param db: psycopg2 connection to the mycroft database
|
||||||
|
:param device_id: uuid
|
||||||
|
:param setting_version_hash:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
sections = get_setting_sections_by_device_id_and_setting_version(db, device_id, setting_version_hash)
|
||||||
|
setting_sections_ids = tuple(map(lambda s: s.id, sections))
|
||||||
|
settings = get_setting_by_section_id(db, setting_sections_ids)
|
||||||
|
skill_settings = get_skill_settings_by_device_id_and_version_hash(db, device_id, setting_version_hash)
|
||||||
|
|
||||||
|
# Fills each setting with the correspondent value from the skill setting
|
||||||
|
for skill_setting in skill_settings:
|
||||||
|
s = next(filter(lambda setting: setting.id == skill_setting.setting_id, settings), None)
|
||||||
|
if s:
|
||||||
|
s.value = skill_setting.value
|
||||||
|
|
||||||
|
# Fills each setting with its list of settings
|
||||||
|
for section in sections:
|
||||||
|
section.settings = list(filter(lambda setting: setting.setting_section_id == section.id, settings))
|
||||||
|
|
||||||
|
return sections
|
|
@ -0,0 +1 @@
|
||||||
|
SELECT * FROM skill.setting WHERE setting_section_id IN %(setting_section_ids)s
|
|
@ -0,0 +1,6 @@
|
||||||
|
SELECT set_section.* FROM device.device dev
|
||||||
|
INNER JOIN device.device_skill dev_skill ON dev.id = dev_skill.device_id
|
||||||
|
INNER JOIN skill.skill skill ON dev_skill.skill_id = skill.id
|
||||||
|
INNER JOIN skill.setting_version set_version ON skill.id = set_version.skill_id
|
||||||
|
INNER JOIN skill.setting_section set_section ON set_version.id = set_section.skill_version_id
|
||||||
|
WHERE dev.id = %(device_id)s AND set_version.version_hash = %(setting_version_hash)s
|
|
@ -0,0 +1,6 @@
|
||||||
|
SELECT skill_set.* FROM device.device dev
|
||||||
|
JOIN device.device_skill dev_skill ON dev.id = dev_skill.device_id
|
||||||
|
JOIN device.skill_setting skill_set ON dev_skill.id = skill_set.device_skill_id
|
||||||
|
JOIN skill.skill skill ON dev_skill.skill_id = skill.id
|
||||||
|
JOIN skill.setting_version ver ON skill.id = ver.skill_id
|
||||||
|
WHERE ver.version_hash = %(setting_version_hash)s AND dev.id = %(device_id)s
|
Loading…
Reference in New Issue