class OWdictyExpress(OWWidget): name = "dictyExpress" description = "Time-course gene expression data" icon = "../widgets/icons/OWdictyExpress.png" want_main_area = True priority = 3 class Inputs: pass class Outputs: etc_data = Output("Data", Table) class Error(OWWidget.Error): unreachable_host = Msg('Host not reachable') invalid_credentials = Msg('Invalid credentials') username = settings.Setting('') # password = settings.Setting('') gene_as_attr_name = settings.Setting(0) selected_item = settings.Setting(None, schema_only=True) auto_commit = settings.Setting(False, schema_only=True) def __init__(self): super().__init__() self.res = None self.organism = '44689' self.server = 'https://dictyexpress.research.bcm.edu' self.headerLabels = [x[1] for x in Labels] self.searchString = "" self.items = [] self.progress_bar = None # threads self.threadpool = QThreadPool() # Login Section box = gui.widgetBox(self.controlArea, 'Login') self.namefield = gui.lineEdit(box, self, "username", "Username:"******"password", "Password:"******"Output", addSpace=True) gui.radioButtonsInBox(box, self, "gene_as_attr_name", ["Genes in rows", "Genes in columns"], callback=self.invalidate) self.controlArea.layout().addWidget(h_line()) self.refresh_button = gui.button(self.controlArea, self, "Refresh", callback=self.refresh) self.handle_cache_button(True) gui.rubber(self.controlArea) self.commit_button = gui.auto_commit(self.controlArea, self, "auto_commit", "&Commit", box=False) # Experiment Section label = QLabel("Available projects:") my_font = QFont() my_font.setBold(True) label.setFont(my_font) self.mainArea.layout().addWidget(label) self.mainArea.layout().addWidget(h_line()) self.filter = gui.lineEdit(self.mainArea, self, "searchString", "Filter:", callbackOnType=True, callback=self.search_update) self.experimentsWidget = QTreeWidget(alternatingRowColors=True, rootIsDecorated=False, uniformRowHeights=True, sortingEnabled=True) self.experimentsWidget.setItemDelegateForColumn( 0, gui.IndicatorItemDelegate(self, role=Qt.DisplayRole)) self.experimentsWidget.selectionModel().selectionChanged.connect( self.on_selection_changed) self.experimentsWidget.setHeaderLabels(self.headerLabels) self.mainArea.layout().addWidget(self.experimentsWidget) self.auth_set() self.connect() self.sizeHint() def sizeHint(self): return QSize(1400, 680) def auth_set(self): self.passfield.setDisabled(not self.username) def auth_changed(self): self.auth_set() self.connect() def refresh(self): self.reset() self.load_experiments() def reset(self): self.experimentsWidget.clear() # clear QTreeWidget self.items = [] # self.lastSelected = None self.searchString = "" def search_update(self): parts = self.searchString.split() for item in self.items: item.setHidden(not all(s in item for s in parts)) def progress_advance(self): # GUI should be updated in main thread. That's why we are calling advance method here assert threading.current_thread() == threading.main_thread() if self.progress_bar: self.progress_bar.advance() def handle_error(self, ex): self.progress_bar.finish() self.setStatusMessage('') if isinstance(ex, ConnectionError) or isinstance(ex, ValueError): self.Error.unreachable_host() print(ex) def load_experiments_result(self, experiments): self.load_tree_items(experiments) self.progress_bar.finish() self.setStatusMessage('') def connect(self): self.res = None self.Error.clear() self.reset() self.handle_cache_button(False) user, password = resolwe.DEFAULT_EMAIL, resolwe.DEFAULT_PASSWD if self.username or self.password: user, password = self.username, self.password try: self.res = resolwe.connect(user, password, self.server, 'genesis') except resolwe.ResolweAuthException: self.Error.invalid_credentials() else: self.load_experiments() self.handle_cache_button(True) def load_experiments(self): if self.res: # init progress bar self.progress_bar = gui.ProgressBar(self, iterations=2) # status message self.setStatusMessage('downloading experiments') worker = Worker(self.res.fetch_etc_objects, progress_callback=True) worker.signals.progress.connect(self.progress_advance) worker.signals.result.connect(self.load_experiments_result) worker.signals.error.connect(self.handle_error) # move download process to worker thread self.threadpool.start(worker) def load_tree_items(self, list_of_exp): self.items = [ CustomTreeItem(self.experimentsWidget, item) for item in list_of_exp ] for i in range(len(self.headerLabels)): self.experimentsWidget.resizeColumnToContents(i) self.set_cached_indicator() self.set_selected() def set_selected(self): for item in self.items: if self.selected_item and item.gen_data_id == self.selected_item: self.experimentsWidget.setCurrentItem(item) def on_selection_changed(self): self.invalidate() def invalidate(self): self.commit() def handle_cache_button(self, handle): self.refresh_button.setEnabled(handle) def send_to_output(self, result): self.progress_bar.finish() self.setStatusMessage('') etc_json, table_name = result # convert to table data = etc_to_table(etc_json, bool(self.gene_as_attr_name)) # set table name data.name = table_name # match genes gene_matcher = GeneMatcher(str(self.organism)) if not bool(self.gene_as_attr_name): if 'Gene' in data.domain: gene_column = data.domain['Gene'] gene_names = data.get_column_view(gene_column)[0] gene_matcher.genes = gene_names domain_ids = Domain([], metas=[StringVariable(ENTREZ_ID)]) data_ids = [[str(gene.gene_id) if gene.gene_id else '?'] for gene in gene_matcher.genes] table_ids = Table(domain_ids, data_ids) data = Table.concatenate([data, table_ids]) data.attributes[GENE_ID_COLUMN] = ENTREZ_ID else: gene_matcher.match_table_attributes(data) data.attributes[GENE_ID_ATTRIBUTE] = ENTREZ_ID # add table attributes data.attributes[TAX_ID] = str(self.organism) data.attributes[GENE_AS_ATTRIBUTE_NAME] = bool(self.gene_as_attr_name) # reset cache indicators self.set_cached_indicator() # send data to the output signal self.Outputs.etc_data.send(data) def commit(self): self.Error.clear() selected_item = self.experimentsWidget.currentItem( ) # get selected TreeItem self.selected_item = selected_item.gen_data_id if selected_item: # init progress bar self.progress_bar = gui.ProgressBar(self, iterations=1) # status message self.setStatusMessage('downloading experiment data') worker = Worker( self.res.download_etc_data, selected_item.gen_data_id, table_name=selected_item.data_name, progress_callback=True, ) worker.signals.progress.connect(self.progress_advance) worker.signals.result.connect(self.send_to_output) worker.signals.error.connect(self.handle_error) # move download process to worker thread self.threadpool.start(worker) def set_cached_indicator(self): cached = self.res.get_cached_ids() for item in self.items: if item.gen_data_id in cached: item.setData(0, Qt.DisplayRole, " ") else: item.setData(0, Qt.DisplayRole, "")
class OWdictyExpress(OWWidget, ConcurrentWidgetMixin): name = "dictyExpress" description = "Time-course gene expression data" icon = "../widgets/icons/OWdictyExpress.svg" want_main_area = True priority = 20 class Inputs: pass class Outputs: etc_data = Output("Data", Table) class Error(OWWidget.Error): unreachable_host = Msg('Host not reachable') invalid_credentials = Msg('Invalid credentials') gene_as_attr_name = settings.Setting(0) selected_item = settings.Setting(None, schema_only=True) auto_commit = settings.Setting(False, schema_only=True) def __init__(self): super().__init__() ConcurrentWidgetMixin.__init__(self) self._res: Optional[genapi.GenAPI] = None self.organism = '44689' self.server = 'https://dictyexpress.research.bcm.edu' self.headerLabels = [x[1] for x in Labels] self.searchString = "" self.items = [] self.genapi_pub_auth = { 'url': genapi.DEFAULT_URL, 'username': genapi.DEFAULT_EMAIL, 'password': genapi.DEFAULT_PASSWD, } # Login Section box = gui.widgetBox(self.controlArea, 'Sign in') self.user_info = gui.label(box, self, '') self.server_info = gui.label(box, self, '') box = gui.widgetBox(box, orientation=Qt.Horizontal) self.sign_in_btn = gui.button(box, self, 'Sign In', callback=self.sign_in, autoDefault=False) self.sign_out_btn = gui.button(box, self, 'Sign Out', callback=self.sign_out, autoDefault=False) box = gui.widgetBox(self.controlArea, "Output") gui.radioButtonsInBox(box, self, "gene_as_attr_name", ["Genes in rows", "Genes in columns"], callback=self.invalidate) self.clear_cache_btn = gui.button(self.controlArea, self, "Clear cache", autoDefault=False, callback=self.clear_cache) gui.rubber(self.controlArea) self.commit_button = gui.auto_commit(self.controlArea, self, "auto_commit", "&Commit", box=False) # Experiment Section label = QLabel("Available projects:") my_font = QFont() my_font.setBold(True) label.setFont(my_font) self.mainArea.layout().addWidget(label) self.filter = gui.lineEdit(self.mainArea, self, "searchString", "Filter:", callbackOnType=True, callback=self.search_update) self.experimentsWidget = QTreeWidget(alternatingRowColors=True, rootIsDecorated=False, uniformRowHeights=True, sortingEnabled=True) self.experimentsWidget.setItemDelegateForColumn( 0, gui.IndicatorItemDelegate(self, role=Qt.DisplayRole)) self.experimentsWidget.selectionModel().selectionChanged.connect( self.on_selection_changed) self.experimentsWidget.setHeaderLabels(self.headerLabels) self.mainArea.layout().addWidget(self.experimentsWidget) self.sign_in(silent=True) self.sizeHint() def sizeHint(self): return QSize(1400, 680) @property def res(self): return self._res @res.setter def res(self, value: genapi.GenAPI): if isinstance(value, genapi.GenAPI): self._res = value self.Error.clear() self.reset() self.load_experiments() self.update_user_status() self.Outputs.etc_data.send(None) def sign_in(self, silent=False): dialog = SignIn(self, server_type='genesis') if silent: dialog.sign_in() if dialog.resolwe_instance is not None: self.res = dialog.resolwe_instance else: self.res = connect(**self.genapi_pub_auth, server_type=resolwe.GENESIS_PLATFORM) if not silent and dialog.exec_(): self.res = dialog.resolwe_instance def sign_out(self): # Remove username and password cm = get_credential_manager(resolwe.GENESIS_PLATFORM) del cm.username del cm.password # Use public credentials when user signs out self.res = connect(**self.genapi_pub_auth, server_type=resolwe.GENESIS_PLATFORM) def update_user_status(self): cm = get_credential_manager(resolwe.GENESIS_PLATFORM) if cm.username: user_info = f"User: {cm.username}" self.sign_in_btn.setEnabled(False) self.sign_out_btn.setEnabled(True) else: user_info = 'User: Anonymous' self.sign_in_btn.setEnabled(True) self.sign_out_btn.setEnabled(False) self.user_info.setText(user_info) self.server_info.setText(f'Server: {self.res._gen.url[8:]}') def clear_cache(self): resolwe.GenAPI.clear_cache() self.reset() self.load_experiments() def reset(self): self.experimentsWidget.clear() # clear QTreeWidget self.items = [] # self.lastSelected = None self.searchString = "" def search_update(self): parts = self.searchString.split() for item in self.items: item.setHidden(not all(s in item for s in parts)) def on_exception(self, ex): if isinstance(ex, ConnectionError) or isinstance(ex, ValueError): self.Error.unreachable_host() print(ex) def on_done(self, results): if isinstance(results, list): self.load_tree_items(results) elif isinstance(results, tuple): self.send_to_output(results) def load_experiments(self): if self.res: self.start(self.res.fetch_etc_objects) def load_tree_items(self, list_of_exp): self.items = [ CustomTreeItem(self.experimentsWidget, item) for item in list_of_exp ] for i in range(len(self.headerLabels)): self.experimentsWidget.resizeColumnToContents(i) self.set_cached_indicator() self.set_selected() def set_selected(self): for item in self.items: if self.selected_item and item.gen_data_id == self.selected_item: self.experimentsWidget.setCurrentItem(item) def on_selection_changed(self): self.invalidate() def invalidate(self): self.commit() def send_to_output(self, result): etc_json, table_name = result # convert to table data = etc_to_table(etc_json, bool(self.gene_as_attr_name)) # set table name data.name = table_name # match genes gene_matcher = GeneMatcher(str(self.organism)) if not bool(self.gene_as_attr_name): if 'Gene' in data.domain: data = gene_matcher.match_table_column( data, 'Gene', StringVariable(ENTREZ_ID)) data.attributes[GENE_ID_COLUMN] = ENTREZ_ID else: data = gene_matcher.match_table_attributes(data) data.attributes[GENE_ID_ATTRIBUTE] = ENTREZ_ID # add table attributes data.attributes[TAX_ID] = str(self.organism) data.attributes[GENE_AS_ATTRIBUTE_NAME] = bool(self.gene_as_attr_name) # reset cache indicators self.set_cached_indicator() # send data to the output signal self.Outputs.etc_data.send(data) def commit(self): self.Error.clear() selected_items = self.experimentsWidget.selectedItems( ) # get selected TreeItem if len(selected_items) < 1: self.Outputs.etc_data.send(None) return selected_item = selected_items[0] self.selected_item = selected_item.gen_data_id self.start(self.res.download_etc_data, selected_item.gen_data_id, table_name=selected_item.data_name) def set_cached_indicator(self): cached = self.res.get_cached_ids() for item in self.items: if item.gen_data_id in cached: item.setData(0, Qt.DisplayRole, " ") else: item.setData(0, Qt.DisplayRole, "")