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 skill
pull/30/head
Matheus Lima 2018-12-26 18:15:52 -03:00
parent f24946ab30
commit acb77b1566
13 changed files with 204 additions and 9 deletions

View File

@ -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:

View File

@ -0,0 +1,2 @@
from .device import get_device_by_id
from .device import get_devices_by_account_id

View File

@ -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))

View File

@ -0,0 +1 @@
SELECT * FROM device.device WHERE id = %(device_id)s

View File

@ -0,0 +1 @@
SELECT * FROM device.device WHERE account_id = %(account_id)s

View File

@ -0,0 +1 @@
from .db import get_view_connection

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
SELECT * FROM skill.setting WHERE setting_section_id IN %(setting_section_ids)s

View File

@ -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

View File

@ -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