import enum
import os
from schrodinger.application.livedesign import login
from requests.exceptions import HTTPError
# Constants used in the LiveDesign API
KEY_ID = 'id'
READ_ONLY = 'READ_ONLY'
FILE = 'FILE'
DEFAULT = 'DEFAULT'
ATTACHMENT = 'ATTACHMENT'
IMAGE = 'IMAGE'
THREE_D = 'THREE_D'
LD_FILE_TYPES = {ATTACHMENT, IMAGE, THREE_D}
# LiveDesign model command types
[docs]class CommandType(enum.Enum):
normal = 'NORMAL'
realtime = 'REALTIME'
click_to_run = 'CLICK_TO_RUN'
clustering = 'CLUSTERING'
def __str__(self):
return self.value
[docs]def upload_ld_attachment(file_path,
project_id,
ld_client,
remote_file_name=None,
file_type=ATTACHMENT):
"""
Upload a file to LiveDesign as an attachment and return the corresponding
attachment ID.
:param file_path: the path to the file to be uploaded
:type file_path: str
:param project_id: the ID of the LiveDesign project that the file should be
uploaded to
:type project_id: int
:param ld_client: an instance of the LD client to use for the upload
:type ld_client: ldclient.client.LDClient
:param remote_file_name: the name of the attachment once uploaded;
`None`, use the (base) name of the file path
:type remote_file_name: str or NoneType
:param file_type: Type of file being uploaded. Param should be an
image, attachment, or 3D structure in LD_FILE_TYPES.
:type file_type: str
:return: the attachment ID
:rtype: str
"""
if remote_file_name is None:
remote_file_name = os.path.basename(file_path)
assert file_type in LD_FILE_TYPES
attach_map = ld_client.get_or_create_attachment(
file_path,
file_type=file_type,
project_ids=[project_id],
remote_file_name=remote_file_name)
return attach_map[KEY_ID]
[docs]def get_template_vars(template_data, template_class):
"""
Provided a list containing LiveDesign attachment IDs and names, generate and
return a list of template variables.
:param template_data: a list of 2-`tuple` containing LD attachment IDs and
corresponding names
:type template_data: `list` of 2-`tuple` of (`int`, `str`)
:param template_class: the template class to initialize, from the downloaded
ldclient.models module.
:type template_class: class
:return: a list of template variables
:rtype: `list` of `ldclient.models.ModelTemplateVar`
"""
template_vars = []
for attachment_id, name in template_data:
template_var = template_class(tag=READ_ONLY,
type=FILE,
name=name,
data=attachment_id)
template_vars.append(template_var)
return template_vars
[docs]def get_model_by_name(ld_client, model_name, include_archived=True):
"""
Retrieves a single model. Used in KNIME.
:param ld_client: an instance of the LD client to get models
:type ld_client: `ldclient.client.LDClient`
:param model_name: Name of the model to retrieve
:type model_name: str
:param include_archived: Whether to include archived models or not. If
False, ignore archived models
:type include_archived: bool
:return: Matched model object
:rtype: `models.Model`
"""
def is_model_valid(model):
'''
Checks if the given model's name matches model_name
If the name matches, but if include_archived is False and if the matched
model is archived, then return False
'''
if model.name == model_name:
return True if include_archived else not model.archived
return False
matching_models = list(filter(is_model_valid, ld_client.models()))
if len(matching_models) == 0:
raise RuntimeError("No models found with name: {0}".format(model_name))
if len(matching_models) > 1:
raise RuntimeError(
"Multiple models found with name: {0}".format(model_name))
return matching_models[0]
[docs]def upload_ld_model(name,
user_name,
folder,
project_id,
template_vars,
description,
protocol_id,
ld_client,
model_class,
model_recursive_class,
command_type=CommandType.normal,
predictions=None,
model_id=None,
overwrite=False):
"""
Create a LiveDesign model and upload it to a LiveDesign server.
:param name: the name of the model
:type name: `str`
:param user_name: the name of the user uploading this model
:type user_name: `str`
:param folder: the directory path in which the model should be created on
the LiveDesign server
:type folder: `str`
:param project_id: the ID of the LiveDesign project that the file should be
uploaded to
:type project_id: `int`
:param template_vars: a list of template variables containing attachment
data
:rtype: `list` of `ldclient.models.ModelTemplateVar`
:param description: a description of the model
:type description: `str`
:param protocol_id: the ID of the parent protocol for this model
:type protocol_id: `int`
:param ld_client: an instance of the LD client to use for the upload
:type ld_client: `ldclient.client.LDClient`
:param model_class: the model class, from the downloaded ldclient.models
module
:type model_class: class
:param model_recursive_class: the "model recursive class" that is necessary
to build the model instance
:type: model_recursive_class: :class: `ModelRecursive`
:param command_type: Command type of the model to be uploaded
:type command_type: CommandType
:param predictions: the predictions to be added to the model
:type predictions: `list` of `ldclient.models.ModelReturn`
:param model_id: Id of the model to be updated. If model_id is specified,
'overwrite' option will be ignored.
:type model_id: int
:param overwrite: If true and if exactly one model with 'name' already
exists, then instead of creating a new one, update the model.
If no model or more than one model exist with same name, then
create a new model. If 'model_id' is specified, this option will be
ignored and always the given model_id is updated.
:type overwrite: bool
:return: a tuple containing the model instance uploaded to the LiveDesign
server (if available) and a message about the status of the upload
:rtype: tuple[ldclient.models.Model or None, str]
"""
default_command_type = model_recursive_class(DEFAULT, str(command_type))
command_queue = model_recursive_class(DEFAULT, 'SYNC')
if not predictions:
predictions = []
model = model_class(name=name,
archived=False,
published=True,
command_type=default_command_type,
command_queue=command_queue,
user=user_name,
folder=folder,
returns=predictions,
project_ids=[project_id],
template_vars=template_vars,
description=description,
parent=protocol_id)
try:
if model_id:
model.id = model_id
model = ld_client.update_model(model_id, model)
elif overwrite:
model = ld_client.create_or_update_model(model)
else:
model = ld_client.create_model(model)
return model, ''
except HTTPError as exc:
code = exc.response.status_code
msg = f'Export resulted in HTTP error {code}.'
if 'Permission denied' in str(exc):
msg += (' Please make sure your account has the necessary'
' permissions to perform this operation.')
return None, msg
[docs]def get_uploaded_model_url(host, model_id, ld_version):
"""
Gets the URL for a model the user has uploaded
:param host: host name for model
:type host: str
:param model_id: model's ID number
:type model_id: str
:param ld_version: Version of LD used
:type ld_version: login.Version
:return: URL for the user's uploaded model
:rtype: str
"""
if ld_version >= login.LD_VERSION_NEW_GLIDE_MODEL_URL:
path = 'models'
else:
path = 'extprops'
return f'{host}/admin/{path}/ldmodel/{model_id}/change/'
[docs]def get_uploaded_ligand_designer_url(host, model_id):
"""
Gets the URL for a ligand designer config the user has uploaded
:param host: host name for model
:type host: str
:param model_id: model's ID number
:type model_id: str
:param ld_version: Version of LD used
:type ld_version: login.Version
:return: URL for the user's uploaded model
:rtype: str
"""
return f'{host}/admin/liganddesignerconfigurations/liganddesignerconfiguration/{model_id}/change/'