source: orange-bioinformatics/_bioinformatics/widgets/OWPIPAx.py @ 1726:6778e0225b86

Revision 1726:6778e0225b86, 19.5 KB checked in by Ales Erjavec <ales.erjavec@…>, 17 months ago (diff)

Added new icons by Peter Cuhalev and replaced existing ones with expanded paths.

RevLine 
[1022]1"""
[1616]2<name>PIPAx</name>
[1051]3<description>Access data from PIPA RNA-Seq database.</description>
[1726]4<icon>icons/PIPA.svg</icon>
[1616]5<priority>35</priority>
[1022]6"""
7
[1654]8from __future__ import absolute_import
[1616]9
[1121]10import sys, os
[1022]11from collections import defaultdict
[1148]12import math
[1616]13from datetime import date
[1121]14
[1654]15from Orange.orng import orngEnviron
[1656]16from Orange.OrangeWidgets import OWGUI
[1654]17from Orange.OrangeWidgets.OWWidget import *
18
19from .. import obiDicty
20
21from .OWPIPA import (MyTreeWidgetItem, ListItemDelegate,
[1617]22                    SelectionSetsWidget, SortedListWidget)
[1121]23
[1616]24try:
25    from ast import literal_eval
26except ImportError:
27    # Compatibility with Python 2.5
28    literal_eval = eval
29
[1121]30
[1069]31def tfloat(s):
32    try:
33        return float(s)
34    except:
35        return None
36
[1616]37
[1022]38class MyTreeWidgetItem(QTreeWidgetItem):
[1069]39
40    def __init__(self, parent, *args):
41        QTreeWidgetItem.__init__(self, parent, *args)
42        self.par = parent
43
[1022]44    def __contains__(self, text):
[1616]45        return any(text.upper() in str(self.text(i)).upper() \
46                   for i in range(self.columnCount()))
47
[1069]48    def __lt__(self, o1):
49        col = self.par.sortColumn()
[1616]50        if col in [8, 9]:  # WARNING: hardcoded column numbers
[1069]51            return tfloat(self.text(col)) < tfloat(o1.text(col))
52        else:
53            return QTreeWidgetItem.__lt__(self, o1)
54
[1022]55
[1616]56# set buffer file
57bufferpath = os.path.join(orngEnviron.directoryNames["bufferDir"], "pipax")
58
[1022]59try:
60    os.makedirs(bufferpath)
61except:
62    pass
[1616]63
[1022]64bufferfile = os.path.join(bufferpath, "database.sq3")
65
[1617]66
67class SelectionByKey(object):
68    """An object stores item selection by unique key values
69    (works only for row selections in list and table models)
70    Example::
71
72        ## Save selection by unique tuple pairs (DisplayRole of column 1 and 2)
73        selection = SelectionsByKey(itemView.selectionModel().selection(),
74                                    key = (1,2))
75        ## restore selection (Possibly omitting rows not present in the model)
76        selection.select(itemView.selectionModel())
77
78    """
79
80    def __init__(self, itemSelection, name="", key=(0,)):
81        self._key = key
82        self.name = name
83        self._selected_keys = []
84        if itemSelection:
85            self.setSelection(itemSelection)
86
87    def _row_key(self, model, row):
88        def key(row, col):
89            return str(model.data(model.index(row, col),
90                                  Qt.DisplayRole).toString())
91
92        return tuple(key(row, col) for col in self._key)
93
94    def setSelection(self, itemSelection):
95        self._selected_keys = [self._row_key(ind.model(), ind.row()) \
96                               for ind in itemSelection.indexes() \
97                               if ind.column() == 0]
98
99    def select(self, selectionModel):
100        model = selectionModel.model()
101        selectionModel.clear()
102        for i in range(model.rowCount()):
103            if self._row_key(model, i) in self._selected_keys:
104                selectionModel.select(model.index(i, 0),
105                    QItemSelectionModel.Select | QItemSelectionModel.Rows)
106
107    def __len__(self):
108        return len(self._selected_keys)
109
110
[1616]111# Mapping from PIPAx.results_list annotation keys to Header names.
112HEADER = [("_cached", ""),
113          ("data_name", "Name"),
114          ("species_name", "Species"),
115          ("strain", "Strain"),
116          ("Experiment", "Experiment"),
117          ("genotype", "Genotype"),
118          ("treatment", "Treatment"),
119          ("growth", "Growth"),
120          ("tp", "Timepoint"),
121          ("replicate", "Replicate"),
122          ("unique_id", "ID"),
123          ("date_rnaseq", "Date RNAseq"),
124          ("adapter_type", "Adapter"),
125          ("experimenter", "Experimenter"),
126          ("band", "Band"),
127          ("polya", "Polya"),
128          ("primer", "Primer"),
129          ("shearing", "Shearing")
130          ]
[1121]131
[1616]132# Index of unique_id
133ID_INDEX = 10
[1121]134
[1616]135# Index of 'date_rnaseq'
136DATE_INDEX = 11
[1121]137
[1616]138SORTING_MODEL_LIST = \
139    ["Strain", "Experiment", "Genotype",
140     "Timepoint", "Growth", "Species",
141     "ID", "Name", "Replicate"]
[1371]142
[1121]143
[1616]144class OWPIPAx(OWWidget):
145    settingsList = ["server", "excludeconstant", "username", "password",
146                    "joinreplicates", "selectionSetsWidget.selections",
147                    "columnsSortingWidget.sortingOrder", "currentSelection",
[1681]148                    "log2", "experimentsHeaderState", "rtypei" ]
[1616]149
150    def __init__(self, parent=None, signalManager=None, name="PIPAx"):
[1022]151        OWWidget.__init__(self, parent, signalManager, name)
152        self.outputs = [("Example table", ExampleTable)]
153
[1029]154        self.username = ""
155        self.password = ""
[1148]156        self.log2 = False
[1681]157        self.rtypei = 5 #hardcoded rpkm mapability polya
[1022]158
159        self.selectedExperiments = []
160        self.buffer = obiDicty.BufferSQLite(bufferfile)
161
162        self.searchString = ""
163        self.excludeconstant = False
[1037]164        self.joinreplicates = False
[1121]165        self.currentSelection = None
[1049]166
[1616]167        self.experimentsHeaderState = \
168                dict(((name, False) for _, name in HEADER[:ID_INDEX + 1]))
169
170        self.result_types = []
171        self.mappings = {}
[1049]172
[1055]173        self.controlArea.setMaximumWidth(250)
174        self.controlArea.setMinimumWidth(250)
175
[1616]176        OWGUI.button(self.controlArea, self, "Reload",
177                     callback=self.Reload)
178        OWGUI.button(self.controlArea, self, "Clear cache",
179                     callback=self.clear_cache)
180
[1121]181        b = OWGUI.widgetBox(self.controlArea, "Experiment Sets")
182        self.selectionSetsWidget = SelectionSetsWidget(self)
[1616]183        self.selectionSetsWidget.setSizePolicy(QSizePolicy.Preferred,
184                                               QSizePolicy.Maximum)
[1121]185        b.layout().addWidget(self.selectionSetsWidget)
[1616]186
187        OWGUI.separator(self.controlArea)
188
[1121]189        b = OWGUI.widgetBox(self.controlArea, "Sort output columns")
190        self.columnsSortingWidget = SortedListWidget(self)
[1616]191        self.columnsSortingWidget.setSizePolicy(QSizePolicy.Preferred,
192                                                QSizePolicy.Maximum)
[1121]193        b.layout().addWidget(self.columnsSortingWidget)
[1616]194        sorting_model = QStringListModel(SORTING_MODEL_LIST)
195        self.columnsSortingWidget.setModel(sorting_model)
196
197        self.columnsSortingWidget.sortingOrder = \
198                ["Strain", "Experiment", "Genotype", "Timepoint"]
199
200        OWGUI.separator(self.controlArea)
[1022]201
[1236]202        box = OWGUI.widgetBox(self.controlArea, 'Expression Type')
[1616]203        self.expressionTypesCB = OWGUI.comboBox(box, self, "rtypei",
204                items=[],
205                callback=self.UpdateResultsList)
[1236]206
[1616]207        OWGUI.checkBox(self.controlArea, self, "excludeconstant",
208                       "Exclude labels with constant values"
209                       )
[1236]210
[1616]211        OWGUI.checkBox(self.controlArea, self, "joinreplicates",
212                       "Average replicates (use median)"
213                       )
[1234]214
[1616]215        OWGUI.checkBox(self.controlArea, self, "log2",
216                       "Logarithmic (base 2) transformation"
217                       )
218
219        self.commit_button = OWGUI.button(self.controlArea, self, "&Commit",
220                                          callback=self.Commit)
[1371]221        self.commit_button.setDisabled(True)
[1049]222
223        OWGUI.rubber(self.controlArea)
224
[1616]225        box = OWGUI.widgetBox(self.controlArea, "Authentication")
[1055]226
[1616]227        OWGUI.lineEdit(box, self, "username", "Username:",
228                       labelWidth=100,
229                       orientation='horizontal',
230                       callback=self.AuthChanged)
231
232        self.passf = OWGUI.lineEdit(box, self, "password", "Password:",
233                                    labelWidth=100,
234                                    orientation='horizontal',
235                                    callback=self.AuthChanged)
236
[1059]237        self.passf.setEchoMode(QLineEdit.Password)
[1022]238
[1616]239        OWGUI.lineEdit(self.mainArea, self, "searchString", "Search",
240                       callbackOnType=True,
241                       callback=self.SearchUpdate)
242
243        self.headerLabels = [t[1] for t in HEADER]
244
[1022]245        self.experimentsWidget = QTreeWidget()
[1170]246        self.experimentsWidget.setHeaderLabels(self.headerLabels)
[1022]247        self.experimentsWidget.setSelectionMode(QTreeWidget.ExtendedSelection)
248        self.experimentsWidget.setRootIsDecorated(False)
249        self.experimentsWidget.setSortingEnabled(True)
[1616]250
251        contextEventFilter = OWGUI.VisibleHeaderSectionContextEventFilter(
252                            self.experimentsWidget, self.experimentsWidget
253                            )
254
[1065]255        self.experimentsWidget.header().installEventFilter(contextEventFilter)
[1616]256        self.experimentsWidget.setItemDelegateForColumn(0,
257                    OWGUI.IndicatorItemDelegate(self, role=Qt.DisplayRole)
258                    )
259
[1121]260        self.experimentsWidget.setAlternatingRowColors(True)
[1616]261
[1121]262        self.connect(self.experimentsWidget.selectionModel(),
[1616]263                 SIGNAL("selectionChanged(QItemSelection, QItemSelection)"),
264                 self.onSelectionChanged)
[1121]265
[1616]266        self.selectionSetsWidget.setSelectionModel(
267                            self.experimentsWidget.selectionModel()
268                            )
[1022]269
270        self.mainArea.layout().addWidget(self.experimentsWidget)
271
272        self.loadSettings()
[1616]273
[1170]274        self.restoreHeaderState()
[1616]275
276        self.connect(self.experimentsWidget.header(),
277                     SIGNAL("geometriesChanged()"),
278                     self.saveHeaderState)
279
[1121]280        self.dbc = None
[1022]281
[1059]282        self.AuthSet()
283
[1121]284        QTimer.singleShot(100, self.UpdateExperiments)
[1022]285
[1616]286        self.resize(800, 600)
[1022]287
[1059]288    def AuthSet(self):
289        if len(self.username):
290            self.passf.setDisabled(False)
291        else:
292            self.passf.setDisabled(True)
293
294    def AuthChanged(self):
295        self.AuthSet()
296        self.ConnectAndUpdate()
297
[1029]298    def ConnectAndUpdate(self):
299        self.Connect()
[1049]300        self.UpdateExperiments(reload=True)
[1029]301
[1022]302    def Connect(self):
[1059]303        self.error(1)
304        self.warning(1)
305
306        def en(x):
307            return x if len(x) else None
308
[1616]309        self.dbc = obiDicty.PIPAx(buffer=self.buffer,
310                                  username=en(self.username),
311                                  password=self.password)
[1059]312
313        #check password
314        if en(self.username) != None:
315            try:
[1616]316                self.dbc.mappings(reload=True)
[1059]317            except obiDicty.AuthenticationError:
318                self.error(1, "Wrong username or password")
319                self.dbc = None
320            except Exception, ex:
[1376]321                print "Error when contacting the PIPA database", ex
322                import traceback
323                print traceback.format_exc()
[1616]324                try:  # maybe cached?
325                    self.dbc.mappings()
[1059]326                    self.warning(1, "Can not access database - using cached data.")
[1616]327                except Exception, ex:
[1059]328                    self.dbc = None
329                    self.error(1, "Can not access database.")
[1022]330
[1049]331    def Reload(self):
332        self.UpdateExperiments(reload=True)
333
334    def clear_cache(self):
[1022]335        self.buffer.clear()
[1049]336        self.Reload()
[1022]337
[1616]338    def rtype(self):
339        """Return selected result template type """
340        if self.result_types:
341            return self.result_types[self.rtypei][0]
[1236]342        else:
343            return "-1"
[1234]344
345    def UpdateExperimentTypes(self):
[1616]346        self.expressionTypesCB.clear()
347        items = [desc for _, desc  in self.result_types]
348        self.expressionTypesCB.addItems(items)
349        self.rtypei = max(0, min(self.rtypei, len(self.result_types) - 1))
[1234]350
[1049]351    def UpdateExperiments(self, reload=False):
[1022]352        self.experimentsWidget.clear()
353        self.items = []
354
355        self.progressBarInit()
356
357        if not self.dbc:
358            self.Connect()
[1616]359
360        mappings = {}
361        result_types = []
362        sucind = False  # success indicator for database index
[1049]363
364        try:
[1616]365            mappings = self.dbc.mappings(reload=reload)
366            result_types = self.dbc.result_types(reload=reload)
[1049]367            sucind = True
368        except Exception, ex:
369            try:
[1616]370                mappings = self.dbc.mappings()
371                result_types = self.dbc.result_types()
[1049]372                self.warning(0, "Can not access database - using cached data.")
373                sucind = True
[1616]374            except Exception, ex:
[1049]375                self.error(0, "Can not access database.")
376
[1059]377        if sucind:
378            self.warning(0)
379            self.error(0)
380
[1616]381        self.mappings = mappings
382        self.result_types = result_types
[1234]383
[1616]384        self.UpdateExperimentTypes()
385
386        results_list = {}
387        try:
388            results_list = self.dbc.results_list(self.rtype(), reload=reload)
389        except Exception, ex:
390            try:
391                results_list = self.dbc.results_list(self.rtype())
392            except Exception, ex:
393                self.error(0, "Can not access database.")
394
395        self.results_list = results_list
396        mappings_key_dict = dict(((m["data_id"], m["id"]), key) \
397                                 for key, m in mappings.items())
398
399        def mapping_unique_id(annot):
400            """Map annotations dict from results_list to unique
401            `mappings` ids.
402            """
403            data_id, mappings_id = annot["data_id"], annot["mappings_id"]
404            return mappings_key_dict[data_id, mappings_id]
[1057]405
[1022]406        elements = []
407        pos = 0
408
[1616]409        for r_id, r_annot in self.results_list.items():
[1022]410            pos += 1
[1616]411            d = defaultdict(lambda: "?", r_annot)
412            row_items = [""] + [d.get(key, "?") for key, _ in HEADER[1:]]
413            date_string = row_items[DATE_INDEX]
414            try:
415                time_dict = literal_eval(date_string)
416            except Exception:
417                time_dict = {}
418
419            if time_dict and "dateUTC" in time_dict and \
420                    "monthUTC" in time_dict and "fullYearUTC" in time_dict:
421                date_rna = date(time_dict["fullYearUTC"],
422                                time_dict["monthUTC"] + 1,  # Why is month 0 based?
423                                time_dict["dateUTC"])
424
[1618]425                row_items[DATE_INDEX] = date_rna.strftime("%x")
[1616]426
427            row_items[ID_INDEX] = mapping_unique_id(r_annot)
428            elements.append(row_items)
429
430            ci = MyTreeWidgetItem(self.experimentsWidget, row_items)
[1069]431
432            self.items.append(ci)
[1022]433
[1156]434        for i in range(len(self.headerLabels)):
[1022]435            self.experimentsWidget.resizeColumnToContents(i)
436
[1616]437        # which is the ok buffer version
438        # FIXME: what attribute to use for version?
439        self.wantbufver = \
440            lambda x, ad=self.results_list: \
441                defaultdict(lambda: "?", ad[x])["date"]
442
443        self.wantbufver = lambda x: "0"
[1049]444
445        self.UpdateCached()
446
[1022]447        self.progressBarFinished()
[1616]448
[1121]449        if self.currentSelection:
450            self.currentSelection.select(self.experimentsWidget.selectionModel())
[1022]451
[1371]452        self.handle_commit_button()
453
[1616]454    def UpdateResultsList(self, reload=False):
455        results_list = {}
456        try:
457            results_list = self.dbc.results_list(self.rtype(), reload=reload)
458        except Exception:
459            results_list = self.dbc.results_list(self.rtype())
460        self.results_list = results_list
461        self.UpdateCached()
462
[1049]463    def UpdateCached(self):
[1059]464        if self.wantbufver and self.dbc:
[1616]465            fn = self.dbc.download_key_function()
466            result_id_key = dict(((m["data_id"], m["mappings_id"]), key) \
467                                 for key, m in self.results_list.items())
[1234]468
[1049]469            for item in self.items:
[1521]470                c = str(item.text(10))
[1616]471                mapping = self.mappings[c]
472                data_id, mappings_id = mapping["data_id"], mapping["id"]
473                r_id = result_id_key[data_id, mappings_id]
474                # Get the buffered version
475                buffered = self.dbc.inBuffer(fn(r_id))
476                value = " " if buffered == self.wantbufver(r_id) else ""
477                item.setData(0, Qt.DisplayRole, QVariant(value))
[1049]478
[1022]479    def SearchUpdate(self, string=""):
480        for item in self.items:
[1616]481            item.setHidden(not all(s in item \
482                                   for s in self.searchString.split())
483                           )
[1022]484
485    def Commit(self):
486        if not self.dbc:
487            self.Connect()
488
[1616]489        pb = OWGUI.ProgressBar(self, iterations=100)
[1022]490
491        table = None
492
493        ids = []
494        for item in self.experimentsWidget.selectedItems():
[1616]495            unique_id = str(item.text(10))
496            annots = self.mappings[unique_id]
497            ids.append((annots["data_id"], annots["id"]))
[1022]498
[1148]499        transfn = None
500        if self.log2:
[1616]501            transfn = lambda x: math.log(x + 1.0, 2)
502
503        reverse_header_dict = dict((name, key) for key, name in HEADER)
504
[1156]505        hview = self.experimentsWidget.header()
[1616]506        shownHeaders = [label for i, label in \
507                        list(enumerate(self.headerLabels))[1:] \
508                        if not hview.isSectionHidden(i)
509                        ]
510
511        allowed_labels = [reverse_header_dict.get(label, label) \
512                          for label in shownHeaders]
513
[1194]514        if self.joinreplicates and "id" not in allowed_labels:
515            # need 'id' labels in join_replicates for attribute names
516            allowed_labels.append("id")
[1616]517
[1371]518        if len(ids):
[1616]519            table = self.dbc.get_data(ids=ids, result_type=self.rtype(),
520                          callback=pb.advance,
521                          exclude_constant_labels=self.excludeconstant,
522#                          bufver=self.wantbufver,
523                          transform=transfn,
524                          allowed_labels=allowed_labels)
[1022]525
[1371]526            if self.joinreplicates:
[1616]527                table = obiDicty.join_replicates(table,
528                    ignorenames=["replicate", "data_id", "mappings_id",
529                                 "data_name", "id", "unique_id"],
530                    namefn=None,
531                    avg=obiDicty.median
532                    )
[1371]533
534            # Sort attributes
535            sortOrder = self.columnsSortingWidget.sortingOrder
[1616]536
537            def sorting_key(attr):
538                atts = attr.attributes
539                return tuple([atts.get(reverse_header_dict[name], "") \
540                              for name in sortOrder])
541
542            attributes = sorted(table.domain.attributes,
543                                key=sorting_key)
544
[1371]545            domain = orange.Domain(attributes, table.domain.classVar)
546            domain.addmetas(table.domain.getmetas())
547            table = orange.ExampleTable(domain, table)
[1616]548
[1654]549            from Orange.orng.orngDataCaching import data_hints
[1616]550            data_hints.set_hint(table, "taxid", "352472")
[1371]551            data_hints.set_hint(table, "genesinrows", False)
[1616]552
[1371]553            self.send("Example table", table)
554
555            self.UpdateCached()
556
[1022]557        pb.finish()
558
[1371]559    def onSelectionChanged(self, selected, deselected):
560        self.handle_commit_button()
[1616]561
[1371]562    def handle_commit_button(self):
[1616]563        self.currentSelection = \
564            SelectionByKey(self.experimentsWidget.selectionModel().selection(),
565                           key=(1, 2, 3, 10))
[1371]566        self.commit_button.setDisabled(not len(self.currentSelection))
567
[1156]568    def saveHeaderState(self):
569        hview = self.experimentsWidget.header()
[1170]570        for i, label in enumerate(self.headerLabels):
571            self.experimentsHeaderState[label] = hview.isSectionHidden(i)
[1616]572
[1156]573    def restoreHeaderState(self):
574        hview = self.experimentsWidget.header()
[1616]575        state = self.experimentsHeaderState
576        for i, label in enumerate(self.headerLabels):
577            hview.setSectionHidden(i, state.get(label, True))
[1156]578            self.experimentsWidget.resizeColumnToContents(i)
[1616]579
[1022]580if __name__ == "__main__":
[1616]581    app = QApplication(sys.argv)
[1234]582    obiDicty.verbose = True
[1616]583    w = OWPIPAx()
[1022]584    w.show()
585    app.exec_()
586    w.saveSettings()
Note: See TracBrowser for help on using the repository browser.