diff --git a/personal_mycroft_backend/backend/device.py b/personal_mycroft_backend/backend/device.py index 32095ee..8ebab8c 100644 --- a/personal_mycroft_backend/backend/device.py +++ b/personal_mycroft_backend/backend/device.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json + from flask_mail import Message from flask import request, Response @@ -172,7 +174,7 @@ def get_uuid(uuid): accessToken=result.get("accessToken"), refreshToken=result.get("refreshToken")) - result = model_to_dict(device) + result = device.as_dict else: result = {} return nice_json(result) @@ -284,4 +286,29 @@ def get_subscriber_voice_url(uuid=""): device.arch = arch return nice_json({"link": ""}) + @app.route("/" + API_VERSION + "/device//skill", methods=['GET', 'PUT']) + @noindex + @donation + @requires_auth + def skill(uuid=""): + with DeviceDatabase(SQL_DEVICES_URI, debug=DEBUG) as device_db: + device = device_db.get_device_by_uuid(uuid) + if device is not None: + if request.method == 'GET': + skills = [] + for skill in device.skills_info: + skills.append(skill.as_dict) + + return nice_json(skills) + if request.method == 'PUT': + data = request.json + skill_metadata = u'' + # Replace parsed json structure with text representation + if 'skillMetadata' in data.keys(): + skill_metadata = str(json.dumps(data['skillMetadata'])) + + data['skillMetadata'] = skill_metadata + device_db.add_skill_info(uuid, data) + + return nice_json({"link": ""}) return app diff --git a/personal_mycroft_backend/database/devices.py b/personal_mycroft_backend/database/devices.py index f42bf18..cfe0f9c 100644 --- a/personal_mycroft_backend/database/devices.py +++ b/personal_mycroft_backend/database/devices.py @@ -13,7 +13,7 @@ # limitations under the License. # from sqlalchemy import Column, ForeignKey, Integer, String, Boolean, Text, \ - Table, Float, create_engine + Table, Float, Unicode, create_engine from sqlalchemy.orm import relationship, sessionmaker from sqlalchemy.exc import IntegrityError @@ -88,6 +88,13 @@ primary_key=True) ) +skill_info_devices = Table('skill_info_devices', Base.metadata, + Column('skill_id', ForeignKey('skillinfo.identifier'), + primary_key=True), + Column('device_id', ForeignKey('devices.uuid'), + primary_key=True) + ) + skill_configs = Table('skill_configs', Base.metadata, Column('skill_id', ForeignKey('skills.id'), primary_key=True), @@ -143,6 +150,8 @@ ) + + # classes @@ -213,12 +222,27 @@ class Device(Base): skills = relationship("Skill", back_populates="device", secondary=skill_devices) + skills_info = relationship("SkillInfo", + back_populates="device", + secondary=skill_info_devices) + metrics = relationship("Metric", back_populates="device", secondary=metrics_devices) hotwords = relationship("Hotword", back_populates="device", secondary=hotword_devices) + @property + def as_dict(self): + bucket = model_to_dict(self) + bucket['location'] = model_to_dict(self.location) + bucket['user'] = model_to_dict(self.user) + bucket['setting'] = model_to_dict(self.config) + # TODO: check API definition document + bucket['user']['uuid'] = self.user.id + return bucket + + class Metric(Base): __tablename__ = "metrics" @@ -261,6 +285,40 @@ class Skill(Base): blacklisted = Column(Boolean, default=False) +class SkillInfo(Base): + __tablename__ = "skillinfo" + identifier = Column(String, primary_key=True, nullable=False) + name = Column(String) + description = Column(String) + contributor = Column(String) + display_name = Column(String) + color = Column(String) + skill_gid = Column(String) + icon = Column(String) + skillMetadata = Column(String) + + device = relationship("Device", # order_by="devices.last_seen", + back_populates="skills_info", + secondary=skill_info_devices, uselist=False) + + @property + def as_dict(self): + # this is a placeholder + bucket = { + 'identifier': str(self.identifier), + 'name': str(self.name), + 'description': str(self.description), + 'contributor': str(self.contributor), + 'icon': str(self.icon), + 'display_name': str(self.display_name), + 'color': str(self.color), + 'skill_gid': str(self.skill_gid), + } + if self.skillMetadata is not None and len(self.skillMetadata) > 0: + bucket['skillMetadata'] = json.loads(str(self.skillMetadata)) + return bucket + + class IPAddress(Base): __tablename__ = "ips" created_at = Column(Integer, default=time.time()) @@ -570,6 +628,9 @@ def get_device_by_token(self, token): token).first() return device + def get_skill_info_by_id(self, skill_id): + return self.session.query(SkillInfo).filter(SkillInfo.identifier == skill_id).first() + def add_location(self, uuid, location_data=None): device = self.get_device_by_uuid(uuid) if device is None: @@ -736,6 +797,37 @@ def add_device(self, uuid, name=None, expires_at=None, accessToken=None, return self.commit() + def add_skill_info(self, uuid, skill_info_data): + device = self.get_device_by_uuid(uuid) + if device is None: + return False + skill_id = skill_info_data['identifier'] + skill = self.get_skill_info_by_id(str(skill_id)) + if skill is None: + skill_info = SkillInfo(identifier=str(skill_info_data['identifier']), + name=str(skill_info_data.get('name', u'')), + description=str(skill_info_data.get('description', u'')), + contributor=str(skill_info_data.get('contributor', u'')), + display_name=str(skill_info_data.get('display_name', u'')), + color=str(skill_info_data.get('color', u'')), + skill_gid=str(skill_info_data.get('skill_gid', u'')), + icon=str(skill_info_data.get('icon', u'')), + skillMetadata=str(skill_info_data.get('skillMetadata', u'')) + ) + skill_info.device = device + device.skills_info.append(skill_info) + self.session.add(skill_info) + else: + skill.name = str(skill_info_data.get('name', '')).encode('utf8') + skill.description = str(skill_info_data.get('description', u'')) + skill.contributor = str(skill_info_data.get('contributor', u'')) + skill.display_name = str(skill_info_data.get('display_name', u'')) + skill.color = str(skill_info_data.get('color', u'')) + skill.skill_gid = str(skill_info_data.get('skill_gid', u'')) + skill.icon = str(skill_info_data.get('icon', u'')) + skill.skillMetadata = str(skill_info_data.get('skillMetadata', u'')) + return self.commit() + def total_users(self): return self.session.query(User).count()