def check_device_name_and_version(self): """ Check to see if: a) The connected device is a OpenDrop b) The device firmware matches the host driver API version In the case where the device firmware version does not match, display a dialog offering to flash the device with the firmware version that matches the host driver API version. """ try: self.connect() name = self.control_board.properties['package_name'] if name != self.control_board.host_package_name: raise Exception("Device is not a DropBot DX") host_software_version = self.control_board.host_software_version remote_software_version = self.control_board.remote_software_version # Reflash the firmware if it is not the right version. if host_software_version != remote_software_version: response = yesno( "The DropBot DX firmware version (%s) " "does not match the driver version (%s). " "Update firmware?" % (remote_software_version, host_software_version)) if response == gtk.RESPONSE_YES: self.on_flash_firmware() except Exception, why: logger.warning("%s" % why)
def on_app_exit(self): app = get_app() if self.modified: result = yesno('Protocol %s has unsaved changes. Save now?'\ % app.protocol.name) if result == gtk.RESPONSE_YES: self.save_protocol()
def check_device_name_and_version(self): ''' Check to see if: a) The connected device is a OpenDrop b) The device firmware matches the host driver API version In the case where the device firmware version does not match, display a dialog offering to flash the device with the firmware version that matches the host driver API version. ''' try: self.connect() name = self.control_board.name() if name != "open_drop": raise Exception("Device is not an OpenDrop") host_software_version = self.control_board.host_software_version() remote_software_version = self.control_board.software_version() # Reflash the firmware if it is not the right version. if host_software_version != remote_software_version: response = yesno("The control board firmware version (%s) " "does not match the driver version (%s). " "Update firmware?" % (remote_software_version, host_software_version)) if response == gtk.RESPONSE_YES: self.on_flash_firmware() except Exception, why: logger.warning("%s" % why)
def update_plugin(self, plugin_controller, verbose=False, force=False): app = get_app() server_url = app.get_app_value('server_url') plugin_metadata = plugin_controller.get_plugin_info() package_name = plugin_metadata.package_name plugin_name = plugin_metadata.plugin_name p = PluginRepository(server_url) latest_version = Version(**p.latest_version(package_name, app_version={'major': 1, 'minor': 0, 'micro': 0})) # Check the plugin tag versus the tag of latest version from the # update respository. If they are different, it's a sign that they # the currently installed plugin may be incompatible. if plugin_controller.version.tags != latest_version.tags: if yesno('The currently installed plugin (%s-%s) is from a ' 'different branch and may not be compatible with ' 'this version of Microdrop. Would you like to download ' 'a compatible version?' % (plugin_name, plugin_controller.version) ) == gtk.RESPONSE_YES: return self.download_and_install_plugin(package_name, force=force) elif plugin_controller.version < latest_version: return self.download_and_install_plugin(package_name, force=force) else: message = 'Plugin %s is up to date (version %s)' % ( plugin_name, plugin_controller.version) if verbose: logging.warning(message) logging.info(message) return False
def update_plugin(self, plugin_controller, verbose=False, force=False): app = get_app() server_url = app.get_app_value('server_url') plugin_metadata = plugin_controller.get_plugin_info() package_name = plugin_metadata.package_name plugin_name = plugin_metadata.plugin_name plugin_repo = PluginRepository(server_url) latest_version = Version(**plugin_repo .latest_version(package_name, app_version=APP_VERSION)) # Check the plugin tag versus the tag of latest version from the # update respository. If they are different, it's a sign that they # the currently installed plugin may be incompatible. if plugin_controller.version.tags != latest_version.tags: if yesno('The currently installed plugin (%s-%s) is from a ' 'different branch and may not be compatible with ' 'this version of MicroDrop. Would you like to download ' 'a compatible version?' % (plugin_name, plugin_controller.version) ) == gtk.RESPONSE_YES: return self.download_and_install_plugin(package_name, force=force) elif plugin_controller.version < latest_version: return self.download_and_install_plugin(package_name, force=force) else: message = 'Plugin %s is up to date (version %s)' % ( plugin_name, plugin_controller.version) if verbose: logging.warning(message) logging.info(message) return False
def save_check(self): app = get_app() if self.modified: result = yesno('Device %s has unsaved changes. Save now?' % app.dmf_device.name) if result == gtk.RESPONSE_YES: self.save_dmf_device()
def import_device(self, input_device_path): input_device_path = ph.path(input_device_path).realpath() output_device_path = (input_device_path.parent .joinpath(input_device_path.namebase + '.svg')) overwrite = False if output_device_path.isfile(): result = yesno('Output file exists. Overwrite?') if not result == gtk.RESPONSE_YES: return overwrite = True convert_device_to_svg(input_device_path, output_device_path, use_svg_path=True, detect_connections=True, extend_mm=.5, overwrite=overwrite) self.load_device(output_device_path)
def apply_device_dir(self, device_directory): ''' .. versionchanged:: 2.21 Use :func:`path_helpers.resource_copytree` to support when copying from a module stored in a ``.zip`` archive or ``.egg`` file. ''' app = get_app() # if the device directory is empty or None, set a default if not device_directory: device_directory = (ph.path(app.config.data['data_dir']) .joinpath('devices')) self.set_app_values({'device_directory': device_directory}) if self.previous_device_dir and (device_directory == self.previous_device_dir): # If the data directory hasn't changed, we do nothing return False device_directory = ph.path(device_directory) if self.previous_device_dir: device_directory.makedirs_p() if device_directory.listdir(): result = yesno('Merge?', '''\ Target directory [%s] is not empty. Merge contents with current devices [%s] (overwriting common paths in the target directory)?''' % (device_directory, self.previous_device_dir)) if not result == gtk.RESPONSE_YES: return False original_directory = ph.path(self.previous_device_dir) for d in original_directory.dirs(): copytree(d, device_directory.joinpath(d.name)) for f in original_directory.files(): f.copyfile(device_directory.joinpath(f.name)) original_directory.rmtree() elif not device_directory.isdir(): # if the device directory doesn't exist, copy the skeleton dir if device_directory.parent: device_directory.parent.makedirs_p() # XXX Use `path_helpers.resource_copytree` to support when copying # from a module stored in a `.zip` archive or `.egg` file. ph.resource_copytree('microdrop', 'devices', device_directory) self.previous_device_dir = device_directory return True
def on_button_uninstall_clicked(self, widget, data=None): package_name = self.get_plugin_package_name() response = yesno('Uninstall plugin %s?' % package_name) if response == gtk.RESPONSE_YES: # remove the plugin from he enabled list app = get_app() if package_name in app.config["plugins"]["enabled"]: app.config["plugins"]["enabled"].remove(package_name) plugin_path = self.get_plugin_path() if plugin_path.isdir(): self.controller.uninstall_plugin(plugin_path) self.controller.restart_required = True self.controller.update() app.main_window_controller.info('%s plugin successfully ' 'removed.' % package_name, 'Uninstall plugin') self.controller.dialog.update()
def _firmware_check(): # Offer to reflash the firmware if the major and minor versions # are not not identical. If micro versions are different, the # firmware is assumed to be compatible. See [1] # # [1]: https://github.com/wheeler-microfluidics/base-node-rpc/issues/8 if any([ host_software_version.major != remote_software_version.major, host_software_version.minor != remote_software_version.minor ]): response = yesno( "The DropBot firmware version (%s) does " "not match the driver version (%s). " "Update firmware?" % (remote_software_version, host_software_version)) if response == gtk.RESPONSE_YES: self.on_flash_firmware()
def update_check(self): if self._update_setting() not in ('auto-update', 'check for updates, but ask before ' 'installing'): return app_update_server_url = self.config.data.get(self.name, {}).get( 'server_url', 'http://microfluidics.utoronto.ca/update') logger.debug('[APP UPDATE SERVER] server url: %s' % app_update_server_url) app_repository = AppRepository(app_update_server_url) current_version = Version.fromstring(self.version) try: latest_version = Version(**app_repository.latest_version('microdrop')) except (JSONRPCException, JSONDecodeException, IOError): logger.warning('Could not connect to application update server: ' '%s', app_update_server_url) return if current_version < latest_version: logger.info('Current version: %s. There is a new version ' 'available: %s %s' % (current_version, latest_version, app_repository.server_url + app_repository .latest_package_url( 'microdrop'))) response = yesno(''' There is a new version of Microdrop available (%s, current version: %s). Would you like to download the latest version in your browser?''' % (latest_version, current_version)) if response == gtk.RESPONSE_YES: latest_full_url = (app_repository.server_url + app_repository .latest_package_url('microdrop')) if webbrowser.open_new_tab(latest_full_url): logger.info('Closing app after opening browser to latest ' 'version (%s).' % latest_version) try: self.main_window_controller.on_destroy(None) except AttributeError: raise SystemExit, 'Closing app to allow upgrade installation' else: logger.info('[SUCCESS] software is up-to-date.\n (installed ' 'version: %s, server version: %s)' % (current_version, latest_version))
def apply_device_dir(self, device_directory): app = get_app() # if the device directory is empty or None, set a default if not device_directory: device_directory = (ph.path( app.config.data['data_dir']).joinpath('devices')) self.set_app_values({'device_directory': device_directory}) if self.previous_device_dir and (device_directory == self.previous_device_dir): # If the data directory hasn't changed, we do nothing return False device_directory = ph.path(device_directory) if self.previous_device_dir: device_directory.makedirs_p() if device_directory.listdir(): result = yesno( 'Merge?', '''\ Target directory [%s] is not empty. Merge contents with current devices [%s] (overwriting common paths in the target directory)?''' % (device_directory, self.previous_device_dir)) if not result == gtk.RESPONSE_YES: return False original_directory = ph.path(self.previous_device_dir) for d in original_directory.dirs(): copytree(d, device_directory.joinpath(d.name)) for f in original_directory.files(): f.copyfile(device_directory.joinpath(f.name)) original_directory.rmtree() elif not device_directory.isdir(): # if the device directory doesn't exist, copy the skeleton dir if device_directory.parent: device_directory.parent.makedirs_p() base_path().joinpath('devices').copytree(device_directory) self.previous_device_dir = device_directory return True
def apply_device_dir(self, device_directory): app = get_app() # if the device directory is empty or None, set a default if not device_directory: device_directory = (ph.path(app.config.data['data_dir']) .joinpath('devices')) self.set_app_values({'device_directory': device_directory}) if self.previous_device_dir and (device_directory == self.previous_device_dir): # If the data directory hasn't changed, we do nothing return False device_directory = ph.path(device_directory) if self.previous_device_dir: device_directory.makedirs_p() if device_directory.listdir(): result = yesno('Merge?', '''\ Target directory [%s] is not empty. Merge contents with current devices [%s] (overwriting common paths in the target directory)?''' % (device_directory, self.previous_device_dir)) if not result == gtk.RESPONSE_YES: return False original_directory = ph.path(self.previous_device_dir) for d in original_directory.dirs(): copytree(d, device_directory.joinpath(d.name)) for f in original_directory.files(): f.copyfile(device_directory.joinpath(f.name)) original_directory.rmtree() elif not device_directory.isdir(): # if the device directory doesn't exist, copy the skeleton dir if device_directory.parent: device_directory.parent.makedirs_p() base_path().joinpath('devices').copytree(device_directory) self.previous_device_dir = device_directory return True
def verify_and_install_new_plugin(self, plugin_root, force=False): plugin_metadata = get_plugin_info(plugin_root) if plugin_metadata is None: logging.error('%s does not contain a valid plugin.' % plugin_root) return False logging.info('Installing: %s' % (plugin_metadata, )) app = get_app() installed_plugin_path = (path(app.config.data['plugins']['directory']) .joinpath(plugin_metadata.package_name)) installed_metadata = get_plugin_info(installed_plugin_path) if installed_metadata: logging.info('Currently installed: %s' % (installed_metadata,)) if installed_metadata.version.tags == \ plugin_metadata.version.tags and \ installed_metadata.version >= plugin_metadata.version: # Installed version is up-to-date message = ('Plugin %s is up-to-date (version %s). Skipping ' 'installation.' % (installed_metadata.plugin_name, installed_metadata.version)) logging.info(message) return else: message = ('A newer version (%s) of the %s plugin is available' ' (current version=%s).' % (plugin_metadata.version, plugin_metadata .plugin_name, installed_metadata .version)) logging.info(message) if not force: response = yesno('''%s Would you like to upgrade?''' % message) if response == gtk.RESPONSE_YES: force = True else: return False if force: try: self.uninstall_plugin(installed_plugin_path) count = 1 target_path = installed_plugin_path while installed_plugin_path.exists(): installed_plugin_path = path( '%s%d' % (installed_plugin_path, count)) if target_path != installed_plugin_path: self.rename_queue.append((installed_plugin_path, target_path)) except: raise return False else: # There is no valid version of this plugin currently installed. logging.info('%s is not currently installed' % plugin_metadata.plugin_name) # enable new plugins by default app.config["plugins"]["enabled"].append(plugin_metadata .package_name) try: self.install_plugin(plugin_root, installed_plugin_path) logging.info('%s installed successfully' % plugin_metadata.plugin_name) except Exception, why: logging.error('Error installing plugin. %s.', why)
get_service_names('microdrop') missing_plugins = [] for k, v in p.plugin_data.items(): if k not in enabled_plugins and k not in missing_plugins: missing_plugins.append(k) for i in range(len(p)): for k, v in p[i].plugin_data.items(): if k not in enabled_plugins and k not in missing_plugins: missing_plugins.append(k) if missing_plugins: logging.info('load protocol(%s): missing plugins: %s' % (filename, ", ".join(missing_plugins))) result = yesno('Some data in the protocol "%s" requires ' 'plugins that are not currently installed:' '\n\t%s\nThis data will be ignored unless you ' 'install and enable these plugins. Would you' 'like to permanently clear this data from the ' 'protocol?' % (p.name, ",\n\t".join(missing_plugins))) if result == gtk.RESPONSE_YES: logging.info('Deleting protocol data for missing items') for k, v in p.plugin_data.items(): if k in missing_plugins: del p.plugin_data[k] for i in range(len(p)): for k, v in p[i].plugin_data.items(): if k in missing_plugins: del p[i].plugin_data[k] self.save_protocol() self.modified = False emit_signal("on_protocol_swapped", [app.protocol, p])
def _export_protocol(self): app = get_app() filter_ = gtk.FileFilter() filter_.set_name(' MicroDrop protocols (*.json)') filter_.add_pattern("*.json") dialog = gtk.FileChooserDialog( title="Export protocol", action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) dialog.add_filter(filter_) dialog.set_default_response(gtk.RESPONSE_OK) dialog.set_current_name(app.protocol.name) dialog.set_current_folder( os.path.join(app.get_device_directory(), app.dmf_device.name, "protocols")) response = dialog.run() try: if response == gtk.RESPONSE_OK: filename = ph.path(dialog.get_filename()) if filename.ext.lower() != '.json': filename = filename + '.json' logger = _L() # use logger with method context try: with open(filename, 'w') as output: protocol_dict_to_json(upgrade_protocol(app.protocol), ostream=output, validate=False, json_kwargs={'indent': 2}) except md.protocol.SerializationError, exception: plugin_exception_counts = Counter( [e['plugin'] for e in exception.exceptions]) logger.info('%s: `%s`', exception, exception.exceptions) result = yesno( 'Error exporting data for the following ' 'plugins: `%s`\n\n' 'Would you like to exclude this data and ' 'export anyway?' % ', '.join(sorted(plugin_exception_counts.keys()))) if result == gtk.RESPONSE_YES: # Delete plugin data that is causing serialization # errors. protocol = copy.deepcopy(app.protocol) protocol.remove_exceptions(exception.exceptions, inplace=True) with open(filename, 'w') as output: protocol_dict_to_json(upgrade_protocol( app.protocol), ostream=output, validate=False, json_kwargs={'indent': 2}) else: # Abort export. logger.warn('Export cancelled.') return logger.info('exported protocol to %s', filename) app = get_app() parent_window = app.main_window_controller.view message = 'Exported protocol to:\n\n%s' % filename ok_dialog = gtk.MessageDialog(parent=parent_window, message_format=message, type=gtk.MESSAGE_OTHER, buttons=gtk.BUTTONS_OK) # Increase default dialog size. ok_dialog.set_size_request(450, 150) ok_dialog.props.title = 'Export complete' ok_dialog.props.use_markup = True ok_dialog.run() ok_dialog.destroy() finally: dialog.destroy()