source: orange-bioinformatics/orangecontrib/bio/widgets/OWPIPAx.py @ 1874:b3e32cc5cf6f

Revision 1874:b3e32cc5cf6f, 20.4 KB checked in by Ales Erjavec <ales.erjavec@…>, 7 months ago (diff)

Added new style widget meta descriptions.

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