Source code for pynuance.mix

"""Provides functions to interacte with Nuance Mix Website"""
import json
import time

import requests
from bs4 import BeautifulSoup

from pynuance.libs.nuance_http import nuance_login
from pynuance.libs.languages import LANGUAGES
from pynuance.libs.error import PyNuanceError


@nuance_login("dev")
[docs]def mix_activated(username=None, password=None, cookies_file=None): # pylint: disable=W0613 """Check if the account has access to Nuance Mix. URL: https://developer.nuance.com/mix/nlu/#/models/ :returns: * True means Mix account activated. * False means Mix is being created or not requested :rtype: bool """ # Check mix status url = "https://developer.nuance.com/public/index.php" result = requests.get(url, params={"task": "mix"}, cookies=cookies) # pylint: disable=E0602 # If "You're on the list!" is here, you just have to wait soup = BeautifulSoup(result.text, 'html.parser') waiting_node = soup.find("h4", text="You're on the list!") ok_node = soup.find("h4", text="Congratulations!") if waiting_node.parent.parent.parent.attrs.get('style') != 'display: none': return False elif ok_node.parent.parent.parent.attrs.get('style') != 'display: none': return True else: raise PyNuanceError("Mix account state unknown")
@nuance_login("mix")
[docs]def list_models(username=None, password=None, cookies_file=None): # pylint: disable=W0613 """Get list of models/project from Nuance Mix.""" result = requests.get("https://developer.nuance.com/mix/nlu/api/v1/projects", cookies=cookies) # pylint: disable=E0602 if result.status_code != 200: return None return result.json().get("data", [])
@nuance_login("mix")
[docs]def create_model(name, language, username=None, password=None, cookies_file=None): # pylint: disable=W0613 """Create a new model in Nuance Mix.""" # Check language voices_by_lang = dict([(l['code'], l['voice']) for l in LANGUAGES.values()]) if language not in voices_by_lang: raise PyNuanceError("Error: language should be in " "{}".format(', '.join(voices_by_lang.keys()))) # First request data = {"name": name, "domains": [], "locale": language, "sources": []} headers = {"Content-Type": "application/json;charset=UTF-8"} url = "https://developer.nuance.com/mix/nlu/api/v1/projects" result = requests.post(url, data=json.dumps(data), headers=headers, cookies=cookies) # pylint: disable=E0602 if result.status_code != 200: raise PyNuanceError("Unknown HTTP error on the first request") model_json = result.json() model_id = model_json.get("id") if model_id is None: raise PyNuanceError("The HTTP create request did not return the new model ID") # Second request data = {"session_id": "-1", "project_id": "-1", "page": "/models/", "query_params": {}, "host": "developer.nuance.com", "port": "", "protocol": "https", "category": "model", "action": "create", "label": name, "value": ""} url = "https://developer.nuance.com/mix/nlu/bolt/ubt" result = requests.post(url, data=json.dumps(data), cookies=cookies) # pylint: disable=E0602 res_json = result.json() if res_json != {}: raise PyNuanceError("Unknown HTTP error on the second request") # Third request url_put_3 = "https://developer.nuance.com/mix/nlu/api/v1/projects/{}".format(model_id) requests.put(url_put_3, cookies=cookies) # pylint: disable=E0602 # Result model return model_json
[docs]def get_model_id(name, username=None, password=None, cookies_file=None): """Get model ID from model name. If name is already a model ID, this function can help to validate the existance of the model. Raise if there are 2 or more models with the same name. """ models = list_models(username, password, cookies_file) model_id = None for model in models: if model.get("name") == name and model_id is not None: raise PyNuanceError("There at least two models with the same name. " "Please use the model ID instead") elif model.get("name") == name: model_id = model.get("id") try: if model.get("id") == int(name): model_id = int(name) break except ValueError: pass if model_id is None: raise PyNuanceError("Model '{}' not found".format(name)) return model_id
@nuance_login("mix")
[docs]def delete_model(name, username=None, password=None, cookies_file=None): """Delete a model from model name or model ID""" model_id = get_model_id(name, username, password, cookies_file) data = {"session_id": "-1", "project_id": "-1", "page": "/model/{}/dashboard".format(model_id), "query_params": {}, "host": "developer.nuance.com", "port": "", "protocol": "https", "category": "model", "action": "delete", "label": "cancelled", "value": model_id} url = "https://developer.nuance.com/mix/nlu/bolt/ubt" result = requests.post(url, data=json.dumps(data), cookies=cookies) # pylint: disable=E0602 if result.status_code != 200: raise PyNuanceError("Bad HTTP status code during the model deletion") url = "https://developer.nuance.com/mix/nlu/api/v1/projects/{}".format(model_id) result = requests.delete(url, cookies=cookies) # pylint: disable=E0602 if result.status_code != 204: raise PyNuanceError("Bad HTTP status code during the model deletion")
@nuance_login("mix")
[docs]def upload_model(name, model_file, username=None, password=None, cookies_file=None): """Upload intent file into a Mix model.""" # Get model ID model_id = get_model_id(name, username, password, cookies_file) # Send file with open(model_file, 'rb') as fhm: print("Sending: {}".format(model_file)) url = "https://developer.nuance.com/mix/nlu/api/v1/data/{}/import".format(model_id) files = {'file': fhm} requests.post(url, files=files, cookies=cookies) # pylint: disable=E0602 # Save url = "https://developer.nuance.com/mix/nlu/api/v1/project/{}".format(model_id) requests.put(url, cookies=cookies) # pylint: disable=E0602 time.sleep(1)
@nuance_login("mix")
[docs]def train_model(name, username=None, password=None, cookies_file=None): """Train a given Mix Model""" print("Training: {}".format(name)) # Get model ID model_id = get_model_id(name, username, password, cookies_file) url = "https://developer.nuance.com/mix/nlu/api/v1/nlu/{}/annotations/train".format(model_id) requests.post(url, cookies=cookies) # pylint: disable=E0602
@nuance_login("mix")
[docs]def get_model(name, username=None, password=None, cookies_file=None): """Get model data from Nuance Mix Website""" # Get model ID model_id = get_model_id(name, username, password, cookies_file) url = "https://developer.nuance.com/mix/nlu/api/v1/projects/{}".format(model_id) result = requests.get(url, cookies=cookies) # pylint: disable=E0602 return result.json()
[docs]def model_build_create(name, notes="", username=None, password=None, cookies_file=None): """Create a new model build.""" # Get model ID model_id = get_model_id(name, username, password, cookies_file) # Send Request url = "https://developer.nuance.com/mix/nlu/api/v1/nlu/{}/engine".format(model_id) params = {"notes": notes, "type": "Default", "with_asr": "true"} requests.post(url, params=params, cookies=cookies) # pylint: disable=E0602
# TODO check build status each second to wait completion
[docs]def model_build_list(name, username=None, password=None, cookies_file=None): """Return the list of all builds for a given model""" model = get_model(name, username, password, cookies_file) return model.get("builds", [])
@nuance_login("mix")
[docs]def model_build_attach(name, build_version=None, context_tag="latest", username=None, password=None, cookies_file=None): """Attach model version to a Nuance App For now, only SandBoxApp is supported by pynuance """ # TODO validate context_tag headers = {"Content-Type": "application/json;charset=UTF-8"} # Get model ID model = get_model(name, username, password, cookies_file) model_id = model.get("id") # Get buildID build_id = None if build_version is not None: for build in model.get("builds", []): if int(build_version) == build.get("version"): build_id = build.get("id") break if build_id is None: raise Exception("Build not found") # Get SandBox app ID url = "https://developer.nuance.com/mix/nlu/bolt/applications" params = {"configurations": "true"} result = requests.get(url, params=params, cookies=cookies) # pylint: disable=E0602 app_id = None for app in result.json().get("applications"): if app['name'] == 'Sandbox App': app_id = app['id'] break # Get NMAID url = "https://developer.nuance.com/mix/nlu/bolt/nmaids" result = requests.get(url, cookies=cookies) # pylint: disable=E0602 nmaids = [app['nmaid'] for app in result.json()['data'] if app['app_id'] == app_id] if len(nmaids) < 1: raise PyNuanceError("Can not get nmaid") nmaid = nmaids[0] # Attach data = {"nmaid": nmaid, "project_id": model_id, "tag": context_tag} url = "https://developer.nuance.com/mix/nlu/bolt/applications/{}/configurations".format(app_id) result = requests.post(url, data=json.dumps(data), headers=headers, cookies=cookies) # pylint: disable=E0602 conf_id = result.json().get("id") if conf_id is None: raise PyNuanceError("Can not find configuration ID") data = {"page": "/model/{}/publish".format(model_id), "query_params": {}, "host": "developer.nuance.com", "port": 443, "protocol": "https", "category": "publish", "action": "associate-to-app", "label": "finish", "value": ""} url = "https://developer.nuance.com/mix/nlu/bolt/ubt" result = requests.post(url, data=json.dumps(data), headers=headers, cookies=cookies) # pylint: disable=E0602 url = ("https://developer.nuance.com/mix/nlu/bolt/applications/{}/" "configurations/{}/settings".format(app_id, conf_id)) result = requests.put(url, data=json.dumps({}), headers=headers, cookies=cookies) # pylint: disable=E0602 url = ("https://developer.nuance.com/mix/nlu/bolt/applications/{}/" "configurations/{}/models".format(app_id, conf_id)) if build_id is not None: data = [{"model_id": "{}".format(model_id), "build_id": "{}".format(build_id), "build_version": "{}".format(build_version)}] else: data = [{"model_id": "{}".format(model_id)}] requests.put(url, data=json.dumps(data), headers=headers, cookies=cookies) # pylint: disable=E0602