selene-backend/api/market/market_api/endpoints/available_skills.py

108 lines
3.8 KiB
Python

"""Endpoint to provide skill summary data to the marketplace."""
from collections import defaultdict
from http import HTTPStatus
from logging import getLogger
from typing import List
import requests as service_request
from selene.api import APIError, SeleneEndpoint
from .common import RepositorySkill
_log = getLogger(__package__)
class AvailableSkillsEndpoint(SeleneEndpoint):
authentication_required = False
def __init__(self):
super(AvailableSkillsEndpoint, self).__init__()
self.available_skills: List[RepositorySkill] = []
self.response_skills: List[dict] = []
self.skills_in_manifests = defaultdict(list)
def get(self):
try:
self._get_available_skills()
except APIError:
pass
else:
self._build_response_data()
self.response = (self.response_skills, HTTPStatus.OK)
return self.response
def _get_available_skills(self):
"""Retrieve all skills in the skill repository.
The data is retrieved from a database table that is populated with
the contents of a JSON object in the mycroft-skills-data Github
repository. The JSON object contains metadata about each skill.
"""
skill_service_response = service_request.get(
self.config['SERVICE_BASE_URL'] + '/skill/all'
)
if skill_service_response.status_code != HTTPStatus.OK:
self._check_for_service_errors(skill_service_response)
self.available_skills = [
RepositorySkill(**skill) for skill in skill_service_response.json()
]
def _build_response_data(self):
"""Build the data to include in the response."""
if self.request.query_string:
skills_to_include = self._filter_skills()
else:
skills_to_include = self.available_skills
self._reformat_skills(skills_to_include)
self._sort_skills()
def _filter_skills(self) -> list:
"""If search criteria exist, only return those skills that match."""
skills_to_include = []
query_string = self.request.query_string.decode()
search_term = query_string.lower().split('=')[1]
for skill in self.available_skills:
search_term_match = (
search_term is None or
search_term in skill.title.lower() or
search_term in skill.description.lower() or
search_term in skill.summary.lower() or
search_term in [c.lower() for c in skill.categories] or
search_term in [t.lower() for t in skill.tags] or
search_term in [t.lower() for t in skill.triggers]
)
if search_term_match:
skills_to_include.append(skill)
return skills_to_include
def _reformat_skills(self, skills_to_include: List[RepositorySkill]):
"""Build the response data from the skill service response"""
for skill in skills_to_include:
trigger = None
if skill.triggers:
trigger = skill.triggers[0]
skill_info = dict(
icon=skill.icon,
iconImage=skill.icon_image,
isMycroftMade=skill.is_mycroft_made,
isSystemSkill=skill.is_system_skill,
marketCategory=skill.market_category,
name=skill.skill_name,
summary=skill.summary,
title=skill.title,
trigger=trigger
)
self.response_skills.append(skill_info)
def _sort_skills(self):
"""Sort the skills in alphabetical order"""
sorted_skills = sorted(
self.response_skills,
key=lambda skill:
skill['title']
)
self.response_skills = sorted_skills