def _rpc_request(self, function_name, args=None, kwargs=None): """Publish an RPC request to redis, and await response. :rtype: data returned by PluginInstance function called by RPC. """ args = args or [] kwargs = kwargs or {} fn_definition = api_function_definitions[function_name] serialized_args = [] serialized_kwargs = {} for arg_obj, arg_definition in zip(args, fn_definition.params): if isinstance(arg_definition, schemas.Schema): ser_arg = arg_definition.dump(arg_obj) elif isinstance(arg_definition, fields.Field): # Create object with arg value as attribute, so we can validate. temp_obj = type('TempObj', (object, ), {'val': arg_obj}) ser_arg = arg_definition.serialize('val', temp_obj) serialized_args.append(ser_arg) # Set random channel name for response response_channel = str(uuid.uuid4()) message = json.dumps({ 'function': function_name, 'args': serialized_args, 'kwargs': serialized_kwargs, 'response_channel': response_channel }) Logs.message( f"Sending {function_name} Request to Redis Channel {self.channel}") # Subscribe to response channel before publishing message pubsub = self.redis.pubsub(ignore_subscribe_messages=True) pubsub.subscribe(response_channel) self.redis.publish(self.channel, message) timeout = 10 start_time = time.time() while True: message = pubsub.get_message() if time.time() >= start_time + timeout: raise TimeoutError( f"Timeout waiting for response from RPC {function_name}") if not message: continue if message.get('type') == 'message': response_channel = next(iter( pubsub.channels.keys())).decode('utf-8') Logs.message( f"Response received on channel {response_channel}") message_data_str = message['data'].decode('utf-8') response_data = json.loads(message_data_str) pubsub.unsubscribe() output_schema = fn_definition.output if output_schema: deserialized_response = output_schema.load(response_data) else: deserialized_response = None return deserialized_response
async def upload_shapes(self, shape_list): for shape in shape_list: Logs.message(shape.index) response = await nanome.api.shapes.Shape.upload_multiple(shape_list) self.shapes.extend(response) for shape in shape_list: Logs.message(shape.index) return shape_list
def connect(self, host, port): try: self._connection.connect((host, port)) self._connection.setblocking(False) Logs.message("Connected to server", host, port) except (ssl.SSLError, socket.error) as e: self._socket = None self._context = None self._connection = None Logs.error("Cannot connect to plugin server at", host, port, e) return False return True
async def on_run(self): default_url = os.environ.get('DEFAULT_URL') if default_url: jupyter_token = os.environ.get('JUPYTER_TOKEN') url = f'{default_url}?token={jupyter_token}' Logs.message(f'Opening {url}') self.open_url(url) Logs.message("Polling for requests") self.set_plugin_list_button(PluginListButtonType.run, text='Live', usable=False) await self.poll_redis_for_requests(self.redis_channel)
def loading_bar_callback(button): Logs.message("button pressed: " + button.text.value.idle) button.text.value.selected = "Button Pressed!" button.selected = not button.selected self.loadingBar.percentage += .1 self.loadingBar.title = "TITLE" self.loadingBar.description = "DESCRIPTION " + str( self.loadingBar.percentage) self.update_content(button) self.update_content(self.loadingBar)
def message_callback(self, fn_definition, response_channel, response=None): """When response data received from NTS, serialize and publish to response channel.""" output_schema = fn_definition.output serialized_response = {} if output_schema: if isinstance(output_schema, Schema): serialized_response = output_schema.dump(response) elif isinstance(output_schema, fields.Field): # Field that does not need to be deserialized serialized_response = output_schema.serialize(response) json_response = json.dumps(serialized_response) Logs.message(f'Publishing Response to {response_channel}') self.rds.publish(response_channel, json_response)
async def start(self): # Create Redis channel name to send to frontend to publish to redis_channel = os.environ.get('REDIS_CHANNEL') self.redis_channel = redis_channel if redis_channel else str( uuid.uuid4()) Logs.message( f"Starting {self.__class__.__name__} on Redis Channel {self.redis_channel}" ) self.streams = [] self.shapes = [] self.rds = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, decode_responses=True)
def interactive_mode(): Logs.message("Setup utility for Nanome Plugins global configuration") for i in range(len(config_items)): c = config_items[i] Logs.message("==============================") Logs.message(c['name'] + " (" + c['description'] + ")") Logs.message("Current Value:", config.fetch(c['key'])) str = input("New Value (leave empty if unchanged): ") str = str.strip() if str == '': continue value = parse_value(str, c['parse_method']) config.set(c['key'], value)
def _setup_file(): s = "/" home = os.getenv('APPDATA') if (home == None): home = os.getenv('HOME') directory = home + s + ".nanome_lib" config = directory + s + "config.txt" if (not os.path.isdir(directory)): try: os.mkdir(directory) except: return False if (not os.path.isfile(config)): try: Logs.message("Creating config file with path " + config) _setup_clean_config(config) except: return False return config
def process_message(self, message): """Deserialize message and forward request to NTS.""" try: data = json.loads(message.get('data')) except json.JSONDecodeError: error_message = 'JSON Decode Failure' self.send_notification(NotificationTypes.error, error_message) Logs.message(f"Received Request: {data.get('function')}") fn_name = data['function'] serialized_args = data['args'] serialized_kwargs = data['kwargs'] fn_definition = api_function_definitions[fn_name] fn_args = [] fn_kwargs = {} # Deserialize args and kwargs into python classes for ser_arg, schema_or_field in zip(serialized_args, fn_definition.params): if isinstance(schema_or_field, Schema): arg = schema_or_field.load(ser_arg) elif isinstance(schema_or_field, fields.Field): # Field that does not need to be deserialized arg = schema_or_field.deserialize(ser_arg) fn_args.append(arg) response_channel = data['response_channel'] function_to_call = getattr(self, fn_name) # Set up callback function argspec = inspect.getargspec(function_to_call) callback_fn = None if 'callback' in argspec.args: callback_fn = functools.partial(self.message_callback, fn_definition, response_channel) fn_args.append(callback_fn) # Call API function function_to_call(*fn_args, **fn_kwargs) # For non-callback fucntions, ensure message sent back if not callback_fn: self.message_callback(fn_definition, response_channel)
def on_run(self): Logs.message("Run UI Plugin") menu = self.rebuild_menu() self.update_menu(menu)
async def start_process(self, workspace, ff, steps, steepest): if sum(1 for _ in workspace.complexes) == 0: Logs.message('No structures to minimize') return input_file = tempfile.NamedTemporaryFile(delete=False, suffix='.sdf', dir=self.temp_dir.name) constraints_file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt', dir=self.temp_dir.name) output_file = tempfile.NamedTemporaryFile(delete=False, suffix='.pdb', dir=self.temp_dir.name) self.__output_lines = [] self.__updates_done = {} self.__packet_id = 0 (saved_atoms, indices) = self.__save__atoms(input_file.name, workspace) Logs.debug("Wrote input file:", input_file.name) self.__save__constraints(constraints_file.name, saved_atoms) Logs.debug("Wrote constraints file:", constraints_file.name) self.__stream, error = await self.__plugin.create_writing_stream( indices, StreamType.position) if error == StreamCreationError.AtomNotFound: # User deleted atom in time between start_process() and create_writing_stream(). # so lets update the workspace and try again Logs.warning( f"User deleted atoms while setting up process, retrying") updated_workspace = await self.__plugin.request_workspace() await self.start_process(updated_workspace, ff, steps, steepest) return elif error != StreamCreationError.NoError: Logs.error(f"Error while creating stream: {error}") return self.__data_queue = deque() cwd_path = self.__nanobabel_dir exe = 'nanobabel.exe' if IS_WIN else 'nanobabel' exe_path = os.path.join(cwd_path, exe) args = [ 'minimize', '-h', '-l', '20', '-n', str(steps), '-ff', ff, '-i', input_file.name, '-cx', constraints_file.name, '-o', output_file.name ] if IS_WIN: args += ['-dd', 'data'] if steepest: args.append('-sd') Logs.debug(args) p = Process(exe_path, args, True) p.on_error = self.__on_process_error p.on_output = self.__on_process_output p.on_done = self.__on_process_done self.calculation_start_time = time.time() log_data = { 'exe_path': exe_path, 'force_field': ff, 'steps': steps, 'steepest': steepest } Logs.message("Starting Minimization Process", extra=log_data) p.start() self.__process = p self.__process_running = True self.is_running = True
def display_help(): Logs.message("The following arguments are available for Nanome Plugins global configuration") for i in range(len(config_items)): c = config_items[i] Logs.message(c['arg_key'], c['name'], '-', c['description']) Logs.message("\nOr run without arguments for interactive mode")
def start(self): Logs.message("Start UI Plugin") self.create_callbacks()
def hover_callback(button, hovered): Logs.message("button hover: " + button.text.value.idle, hovered)
def select_button_callback(button): button.selected = not button.selected Logs.message("Prefab button pressed: " + button.text.value.idle + " " + str(button._content_id)) self.update_content(button)
def text_submitted_callback(textInput): Logs.message("text input submitted: " + str(textInput.input_text))
def create_tab1(self): self.menu_index = 1 self.previous_menu = None content = nanome.ui.LayoutNode() ln_contentBase = nanome.ui.LayoutNode() ln_label = nanome.ui.LayoutNode() ln_button = nanome.ui.LayoutNode() ln_slider = nanome.ui.LayoutNode() ln_textInput = nanome.ui.LayoutNode() ln_list = nanome.ui.LayoutNode() content.forward_dist = .02 content.layer = 1 ln_label.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_label.padding = (0.01, 0.01, 0.01, 0.01) ln_label.forward_dist = .001 label = nanome.ui.Label() label.text_value = "Press the button..." label.text_color = nanome.util.Color.White() Logs.message("Added Label") ln_button.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_button.padding = (0.01, 0.01, 0.01, 0.01) ln_button.forward_dist = .001 #super styled button button = nanome.ui.Button() button.name = "OpenSubMenu" b_t = button.text b_t.active = True b_t.value.set_all("Spawn menu") b_t.auto_size = False b_t.size = .6 b_t.underlined = True b_t.ellipsis = True b_t.color.idle = nanome.util.Color.Red() b_t.color.highlighted = nanome.util.Color.Blue() b_t.bold.set_all(False) b_t.padding_left = .5 b_t.vertical_align = nanome.util.enums.VertAlignOptions.Middle b_t.horizontal_align = nanome.util.enums.HorizAlignOptions.Left b_m = button.mesh b_m.active = True b_m.color.idle = nanome.util.Color.Blue() b_m.color.highlighted = nanome.util.Color.Red() b_o = button.outline b_o.active = True b_o.color.idle = nanome.util.Color.Red() b_o.color.highlighted = nanome.util.Color.Blue() b_t = button.tooltip b_t.title = "spawn a submenu" b_t.content = "it is useless" b_t.positioning_target = nanome.util.enums.ToolTipPositioning.center button.register_pressed_callback(self.spawn_menu_callback) button.register_hover_callback(self.hover_callback) Logs.message("Added button") ln_slider.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_slider.padding = (0.01, 0.01, 0.01, 0.01) ln_slider.forward_dist = .001 slider = nanome.ui.Slider() slider.register_changed_callback(slider_changed_callback) slider.register_released_callback(slider_released_callback) Logs.message("Added slider") ln_textInput.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_textInput.padding = (0.01, 0.01, 0.01, 0.01) ln_textInput.forward_dist = .001 textInput = nanome.ui.TextInput() textInput.max_length = 30 textInput.register_changed_callback(text_changed_callback) textInput.register_submitted_callback(text_submitted_callback) Logs.message("Added text input") ln_list.sizing_type = nanome.ui.LayoutNode.SizingTypes.ratio ln_list.sizing_value = 0.5 ln_list.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_list.padding = (0.01, 0.01, 0.01, 0.01) ln_list.forward_dist = .03 prefab = nanome.ui.LayoutNode() prefab.layout_orientation = nanome.ui.LayoutNode.LayoutTypes.vertical child1 = nanome.ui.LayoutNode() child1.sizing_type = nanome.ui.LayoutNode.SizingTypes.ratio child1.sizing_value = .3 child1.name = "label" child1.forward_dist = .01 child2 = nanome.ui.LayoutNode() child2.name = "button" child2.forward_dist = .01 prefab.add_child(child1) prefab.add_child(child2) prefabLabel = nanome.ui.Label() prefabLabel.text_value = "Molecule Label" prefabButton = nanome.ui.Button() prefabButton.text.active = True prefabButton.text.value.set_all("Molecule Button") prefabButton.register_pressed_callback(self.select_button_callback) child1.set_content(prefabLabel) child2.set_content(prefabButton) list_content = [] for i in range(0, 10): clone = prefab.clone() list_content.append(clone) list = nanome.ui.UIList() list.display_columns = 1 list.display_rows = 1 list.total_columns = 1 list.items = list_content Logs.message("Added list") content.add_child(ln_contentBase) ln_contentBase.add_child(ln_label) ln_contentBase.add_child(ln_button) ln_contentBase.add_child(ln_slider) ln_contentBase.add_child(ln_textInput) ln_contentBase.add_child(ln_list) ln_label.set_content(label) ln_button.set_content(button) ln_slider.set_content(slider) ln_textInput.set_content(textInput) ln_list.set_content(list) return content
def slider_released_callback(slider): Logs.message("slider released: " + str(slider.current_value))
def text_changed_callback(textInput): Logs.message("text input changed: " + str(textInput.input_text))
def received(self, presenter_info): Logs.message("Presenter:", presenter_info.account_id, presenter_info.account_name, presenter_info.account_email)
def print_callback(self): Logs.message("hbonds complete")
def menu_opened_callback(menu): Logs.message("Menu opened: " + menu.title + " " + str(menu.enabled))
def dropdown_callback(dropdown, item): Logs.message("dropdown item selected: " + str(item.name))
def spawn_menu_callback(button): Logs.message("button pressed: " + button.text.value.idle) self.update_content(button) self.spawn_sub_menu()
def slider_changed_callback(slider): Logs.message("slider changed: " + str(slider.current_value))
def start(self): Logs.message( "Connected to a new session!") # Displays a message in the console
def create_tab2(self): content = nanome.ui.LayoutNode() ln_contentBase = nanome.ui.LayoutNode() ln_label = nanome.ui.LayoutNode() ln_button = nanome.ui.LayoutNode() ln_slider = nanome.ui.LayoutNode() ln_textInput = nanome.ui.LayoutNode() content.forward_dist = .02 content.layer = 1 ln_label.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_label.padding = (0.01, 0.01, 0.01, 0.01) ln_label.forward_dist = .001 label = nanome.ui.Label() label.text_value = "Press the button..." label.text_color = nanome.util.Color.White() Logs.message("Added Label") ln_button.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_button.padding = (0.01, 0.01, 0.01, 0.01) ln_button.forward_dist = .001 button = nanome.ui.Button() button.text.active = True button.text.vertical_align = nanome.util.enums.VertAlignOptions.Middle button.text.horizontal_align = nanome.util.enums.HorizAlignOptions.Middle button.register_pressed_callback(self.loading_bar_callback) button.register_hover_callback(self.hover_callback) Logs.message("Added button") ln_slider.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_slider.padding = (0.01, 0.01, 0.01, 0.01) ln_slider.forward_dist = .001 slider = nanome.ui.Slider() slider.register_changed_callback(slider_changed_callback) slider.register_released_callback(slider_released_callback) Logs.message("Added slider") ln_textInput.padding_type = nanome.ui.LayoutNode.PaddingTypes.ratio ln_textInput.padding = (0.01, 0.01, 0.01, 0.01) ln_textInput.forward_dist = .001 textInput = nanome.ui.TextInput() textInput.max_length = 30 textInput.register_changed_callback(text_changed_callback) textInput.register_submitted_callback(text_submitted_callback) Logs.message("Added text input") prefab = nanome.ui.LayoutNode() prefab.layout_orientation = nanome.ui.LayoutNode.LayoutTypes.vertical child1 = nanome.ui.LayoutNode() child1.sizing_type = nanome.ui.LayoutNode.SizingTypes.ratio child1.sizing_value = .3 child1.name = "label" child1.forward_dist = .01 child2 = nanome.ui.LayoutNode() child2.name = "button" child2.forward_dist = .01 prefab.add_child(child1) prefab.add_child(child2) prefabLabel = nanome.ui.Label() prefabLabel.text_value = "Molecule Label" prefabButton = nanome.ui.Button() prefabButton.text.active = True prefabButton.text.value.set_all("Molecule Button") prefabButton.register_pressed_callback(self.select_button_callback) child1.set_content(prefabLabel) child2.set_content(prefabButton) ln_loading_bar = nanome.ui.LayoutNode(name="LoadingBar") ln_loading_bar.forward_dist = .03 self.loadingBar = ln_loading_bar.add_new_loading_bar() content.add_child(ln_contentBase) ln_contentBase.add_child(ln_label) ln_contentBase.add_child(ln_button) ln_contentBase.add_child(ln_slider) ln_contentBase.add_child(ln_textInput) ln_contentBase.add_child(ln_loading_bar) ln_label.set_content(label) ln_button.set_content(button) ln_slider.set_content(slider) ln_textInput.set_content(textInput) return content
def round_2(self, complexes): Logs.message("round2") complexes[0].name = "c1" complexes[1].name = "c2" complexes[2].name = "c3" self.add_to_workspace(complexes)