Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactions API #377

Merged
merged 45 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
3b5ab60
Interaction API first step
Ramjii Mar 21, 2023
ce0b9a0
verify Interaction test works
mjrosengrant Jul 19, 2023
51cca29
add AsyncInteractionsTest
mjrosengrant Jul 19, 2023
5e768a2
make args optional for InteractionLine definition
mjrosengrant Jul 19, 2023
cea79c1
updates
mjrosengrant Jul 19, 2023
8e16718
-1
mjrosengrant Jul 19, 2023
f3c24ec
Add atom groups to interactions, remove color, add all kinds from che…
Ramjii Jul 20, 2023
4c09fe9
add AsyncInteractionsTest
mjrosengrant Jul 19, 2023
a2c182a
make args optional for InteractionLine definition
mjrosengrant Jul 19, 2023
fbc0315
done callback needs to be optional
mjrosengrant Aug 18, 2023
0b28484
fix callback functions in interactions
mjrosengrant Aug 21, 2023
6f1a9ac
fix upload_multiple
mjrosengrant Aug 21, 2023
00df395
bump serializers version and add visible bool
Ramjii Aug 22, 2023
0455c14
don't error out when interaction re-uploaded
mjrosengrant Aug 22, 2023
1fa2dd8
add section to test toggling visibility
mjrosengrant Aug 23, 2023
72abd97
update interactions test
mjrosengrant Aug 25, 2023
fa3781e
refactor test plugin
mjrosengrant Aug 25, 2023
c18dbdc
Make plugin into a TestCase
mjrosengrant Aug 25, 2023
795f9c9
reorder functions, add traceback to results
mjrosengrant Aug 25, 2023
dad164e
fix InteractionTestAsync
mjrosengrant Aug 28, 2023
d71782c
fix timing issue
mjrosengrant Aug 28, 2023
95714ac
make plugin class import more resilient
mjrosengrant Aug 31, 2023
f8ea6e9
Revert "make plugin class import more resilient"
mjrosengrant Sep 7, 2023
b983455
expose plugin network version table
mjrosengrant Sep 11, 2023
fd5cc1e
Add run interactions
Ramjii Sep 11, 2023
11754e1
Oops
Ramjii Sep 11, 2023
0c1a43a
update debug message
mjrosengrant Sep 20, 2023
5496c54
autopep8
mjrosengrant Sep 29, 2023
c00a3d3
add current_conformer property to Complex class
mjrosengrant Sep 29, 2023
7be8708
update current_conformer
mjrosengrant Sep 29, 2023
07eca41
clean up/ add test for resetting workspace
mjrosengrant Oct 5, 2023
670a632
Add callback to update_workspace
Ramjii Oct 6, 2023
279b1f4
clean up InteractionTestAsync
mjrosengrant Oct 10, 2023
52be30b
reset UpdateWorkspace.py
mjrosengrant Oct 10, 2023
7b6a483
Add signal_calculation_done
Ramjii Oct 13, 2023
513cb46
Forgot to save a file..
Ramjii Oct 13, 2023
a339416
remove debug message
mjrosengrant Oct 17, 2023
3662301
remove unused import
mjrosengrant Oct 18, 2023
bbaca69
remove unused import
mjrosengrant Oct 18, 2023
fe6cd2c
add current_molecule property to Complex
mjrosengrant Nov 1, 2023
c3aeda6
Update nanome/api/structure/complex.py
mjrosengrant Nov 17, 2023
a4287fe
Update nanome/api/interactions/interaction.py
mjrosengrant Nov 17, 2023
8a71986
Correct Van der Waals spelling
mjrosengrant Nov 17, 2023
be25aa1
use update_workspace callback
mjrosengrant Nov 17, 2023
c70433b
fix tests
mjrosengrant Nov 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions nanome/_internal/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class Commands(CommandEnum):
bonds_add_done = auto()
dssp_add_done = auto()
complex_updated = auto()
workspace_updated = auto()
selection_changed = auto()
compute_hbonds_done = auto()

Expand Down Expand Up @@ -93,6 +94,10 @@ class Commands(CommandEnum):
set_shape_result = auto()
delete_shape_result = auto()

# Interactions
create_interactions_result = auto()
get_interactions_response = auto()

# Other
add_volume_done = auto()
load_file_done = auto()
Expand Down Expand Up @@ -174,6 +179,12 @@ class Messages(CommandEnum):
set_shape = auto()
delete_shape = auto()

# Interactions
create_interactions = auto()
get_interactions = auto()
delete_interactions = auto()
interactions_calc_done = auto()

# Other
add_volume = auto()
open_url = auto()
Expand Down Expand Up @@ -201,6 +212,7 @@ class IntegrationCommands(CommandEnum):
import_file = auto()
export_smiles = auto()
import_smiles = auto()
run_interactions = auto()


class Permissions(CommandEnum):
Expand Down
6 changes: 3 additions & 3 deletions nanome/_internal/network/plugin_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, plugin, session_id, queue_in, queue_out, serializer, plugin_i
self._serializer._plugin_id = plugin_id
self._plugin_id = plugin_id
self._command_id = 0
self.__version_table = version_table
self._version_table = version_table

CachedImageField.session = session_id
PluginNetwork._instance = self
Expand Down Expand Up @@ -62,7 +62,7 @@ def send_connect(cls, code, arg):

@classmethod
def send(cls, code, arg, expects_response):
return cls.__send(code, cls._instance.__version_table, arg, expects_response)
return cls.__send(code, cls._instance._version_table, arg, expects_response)

@classmethod
def __send(cls, code, version_table, arg, expects_response):
Expand Down Expand Up @@ -100,7 +100,7 @@ def _receive(self):
return False

received_object, command_hash, request_id = self._serializer.deserialize_command(
payload, self.__version_table)
payload, self._version_table)
if received_object == None and command_hash == None and request_id == None:
return True # Happens if deserialize_command returns None, an error message is already displayed in that case

Expand Down
1 change: 1 addition & 0 deletions nanome/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
from . import shapes
from . import macro
from . import user
from . import interactions
1 change: 1 addition & 0 deletions nanome/api/integration/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def __init__(self):
self.import_file = None
self.export_smiles = None
self.import_smiles = None
self.run_interactions = None

def _call(self, name, request):
callback = getattr(self, name, None)
Expand Down
17 changes: 16 additions & 1 deletion nanome/api/integration/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,20 @@ def deserialize(self, version, context):
return strings


class RunInteractionsSerializer(TypeSerializer):
def version(self):
return 0

def name(self):
return "RunInteractions"

def serialize(self, version, value, context):
pass

def deserialize(self, version, context):
return


class RemoveHydrogenSerializer(TypeSerializer):
def __init__(self):
self.array_serializer = ArrayField()
Expand Down Expand Up @@ -299,7 +313,8 @@ class IntegrationSerializer(TypeSerializer):
Hashes.IntegrationHashes[IntegrationCommands.import_file]: ImportFileSerializer(),
Hashes.IntegrationHashes[IntegrationCommands.generate_molecule_image]: GenerateMoleculeImageSerializer(),
Hashes.IntegrationHashes[IntegrationCommands.export_smiles]: ExportSmilesSerializer(),
Hashes.IntegrationHashes[IntegrationCommands.import_smiles]: ImportSmilesSerializer()
Hashes.IntegrationHashes[IntegrationCommands.import_smiles]: ImportSmilesSerializer(),
Hashes.IntegrationHashes[IntegrationCommands.run_interactions]: RunInteractionsSerializer()
}

def version(self):
Expand Down
19 changes: 19 additions & 0 deletions nanome/api/interactions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from . import *
from nanome._internal.enums import Commands, Messages
from nanome.util import simple_callbacks
from .interaction import Interaction # noqa: F401
from . import serializers, messages # noqa: F401


registered_commands = [
(Commands.create_interactions_result, messages.CreateInteractions(), simple_callbacks.simple_callback_arg_unpack),
(Commands.get_interactions_response, messages.GetInteractions(), simple_callbacks.simple_callback_arg),
]


registered_messages = [
(Messages.create_interactions, messages.CreateInteractions()),
(Messages.get_interactions, messages.GetInteractions()),
(Messages.delete_interactions, messages.DeleteInteractions()),
(Messages.interactions_calc_done, messages.InteractionsCalcDone()),
]
142 changes: 142 additions & 0 deletions nanome/api/interactions/interaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import nanome
from nanome.util import enums
from nanome._internal.network import PluginNetwork
from nanome._internal.enums import Messages


class Interaction(object):
"""
| Class representing a chemical interaction.

:param kind: Enumerator representing the kind of interaction to create
:type kind: :class:`~nanome.util.enums.InteractionType`
:param atom1_idx: Array of integers representing the indices of atoms in group 1
:type atom1_idx: List[int]
:param atom2_idx: Array of integers representing the indices of atoms in group 2
:type atom2_idx: List[int]
:param atom1_conf: Optional conformation for all atoms in group 1
:type atom1_conf: int
:param atom2_conf: Optional conformation for all atoms in group 2
:type atom2_conf: int
"""

def __init__(self, kind=None, atom1_idx_arr=None, atom2_idx_arr=None, atom1_conf=None, atom2_conf=None, visible=True):
self.index = -1
self.kind = kind
self.atom1_idx_arr = atom1_idx_arr
self.atom2_idx_arr = atom2_idx_arr
self.atom1_conformation = atom1_conf
self.atom2_conformation = atom2_conf
self.visible = visible

def upload(self, done_callback=None):
"""
| Upload the interaction to the Nanome App
"""
return self._upload(done_callback)

@classmethod
def upload_multiple(cls, interactions, done_callback=None):
"""
| Upload multiple interactions to the Nanome App
"""
return cls._upload_multiple(interactions, done_callback)

def destroy(self):
"""
| Remove the interaction from the Nanome App and destroy it.
"""
return self._destroy()

@classmethod
def destroy_multiple(cls, interactions):
"""
| Remove multiple interactions from the Nanome App and destroy them.
"""
return cls._destroy_multiple(interactions)

@classmethod
def get(cls, done_callback=None, complexes_idx=None, molecules_idx=None, chains_idx=None,
residues_idx=None, atom_idx=None, type_filter=None):
"""
| Get interactions from Nanome App
| If no structure index is given, all interactions in workspace will be returned
| If any combination of indices is given, all interactions for these sturctures will be returned

:param done_callback: Callback called with the list of interactions received from Nanome
:type done_callback: Callable[List[:class:`~nanome.api.interaction`]]
:param ***_idx: Index or array of indices for a structure type
:type ***_idx: int or List[int]
:param type_filter: Filter to return only one type of interaction
:type type_filter: :class:`~nanome.util.enums.InteractionKind`
"""
args = (
complexes_idx if isinstance(complexes_idx, list) else [complexes_idx] if isinstance(complexes_idx, int) else [],
molecules_idx if isinstance(molecules_idx, list) else [molecules_idx] if isinstance(molecules_idx, int) else [],
chains_idx if isinstance(chains_idx, list) else [chains_idx] if isinstance(chains_idx, int) else [],
residues_idx if isinstance(residues_idx, list) else [residues_idx] if isinstance(residues_idx, int) else [],
atom_idx if isinstance(atom_idx, list) else [atom_idx] if isinstance(atom_idx, int) else [],
type_filter if type_filter is not None else enums.InteractionKind.All
)
return cls._get_interactions(args, done_callback)

def _upload(self, done_callback=None):

def set_callback(line_index):
self.index = line_index
if done_callback is not None:
done_callback(line_index)

id = PluginNetwork._instance.send(Messages.create_interactions, [self], True)
result = nanome.PluginInstance._save_callback(id, set_callback if done_callback else None)
is_async_plugin = nanome.PluginInstance._instance.is_async
if done_callback is None and is_async_plugin:
result.real_set_result = result.set_result
result.set_result = lambda line_index: set_callback(line_index)
def done_callback(line_index): return result.real_set_result(line_index)
return result

@classmethod
def _upload_multiple(cls, interactions, done_callback=None):

def set_callback(indices):
if type(indices) is int:
indices = [indices]
for index, interaction in zip(indices, interactions):
interaction.index = index
if done_callback is not None:
done_callback(indices)

id = PluginNetwork._instance.send(Messages.create_interactions, interactions, True)
result = nanome.PluginInstance._save_callback(id, set_callback if done_callback else None)
if done_callback is None and nanome.PluginInstance._instance.is_async:
result.real_set_result = result.set_result
result.set_result = lambda indices: set_callback(indices)
def done_callback(indices): return result.real_set_result(indices)
return result

def _destroy(self):
PluginNetwork._instance.send(Messages.delete_interactions, [self.index], False)

@classmethod
def _destroy_multiple(cls, interactions):
indices = [x.index for x in interactions]
PluginNetwork._instance.send(Messages.delete_interactions, indices, False)

@classmethod
def _get_interactions(cls, args, done_callback):
def set_callback(interactions=None):
interactions = interactions or []
done_callback(interactions)

id = PluginNetwork._instance.send(Messages.get_interactions, args, True)
fut = nanome.PluginInstance._save_callback(id, set_callback if done_callback else None)
if done_callback is None and nanome.PluginInstance._instance.is_async:
fut.real_set_result = fut.set_result
fut.set_result = lambda interaction_list: set_callback(interaction_list)
def done_callback(interaction_lines): return fut.real_set_result(interaction_lines)
return fut

@classmethod
def signal_calculation_done(cls):
PluginNetwork._instance.send(Messages.interactions_calc_done, None, False)
76 changes: 76 additions & 0 deletions nanome/api/interactions/messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import logging
from nanome._internal import serializer_fields
from . import serializers

logger = logging.getLogger(__name__)


class CreateInteractions(serializer_fields.TypeSerializer):
def __init__(self):
self._interaction = serializers.InteractionSerializer()
self._interaction_array = serializer_fields.ArrayField()
self._interaction_array.set_type(self._interaction)

def version(self):
return 1

def name(self):
return "CreateInteractions"

def serialize(self, version, value, context):
context.write_using_serializer(self._interaction_array, value)

def deserialize(self, version, context):
return context.read_long_array()


class DeleteInteractions(serializer_fields.TypeSerializer):
def version(self):
return 1

def name(self):
return "DeleteInteractions"

def serialize(self, version, value, context):
context.write_long_array(value)

def deserialize(self, version, context):
raise NotImplementedError()


class GetInteractions(serializer_fields.TypeSerializer):
def __init__(self):
self._interaction = serializers.InteractionSerializer()
self._interaction_array = serializer_fields.ArrayField()
self._interaction_array.set_type(self._interaction)

def version(self):
return 1

def name(self):
return "GetInteractions"

def serialize(self, version, value, context):
context.write_long_array(value[0])
context.write_long_array(value[1])
context.write_long_array(value[2])
context.write_long_array(value[3])
context.write_long_array(value[4])
context.write_byte(int(value[5]))

def deserialize(self, version, context):
return context.read_using_serializer(self._interaction_array)


class InteractionsCalcDone(serializer_fields.TypeSerializer):
def version(self):
return 0

def name(self):
return "InteractionsCalcDone"

def serialize(self, version, value, context):
pass

def deserialize(self, version, context):
raise NotImplementedError()
Loading