source: orange-bioinformatics/obiDicty.py @ 1615:8506076ec2e7

Revision 1615:8506076ec2e7, 56.6 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

Added PIPAx api interface.

Line 
1import sys, pprint, time, re
2from itertools import *
3import urllib
4import urllib2
5import orange
6import socket
7import os
8from collections import defaultdict
9import orngServerFiles
10import pickle
11
12defaddress = "http://bcm.fri.uni-lj.si/microarray/api/index.php?"
13#defaddresspipa = "https://pipa.fri.uni-lj.si/pipa/script/api/orange.py?action="
14defaddresspipa = "https://pipa.fri.uni-lj.si/PIPA/PIPAapi/PIPAorange.py"
15defaddresspipa_coverage = "https://pipa.biolab.si/tbrowse/api/pipa.py"
16
17pipaparuser = "pipa_username"
18pipaparpass = "pipa_password"
19
20#utility functions - from Marko's mMisc.py
21
22def splitN(origsize, maxchunk):
23    """
24    Splits an integer into chunks of given size. Each created chunk
25    except possibly the last one is of maximum allowed size.
26    Chunks are returned in list.
27    """
28    l = [maxchunk]*(origsize/maxchunk)
29    a = origsize % maxchunk
30    if a > 0: 
31        l.append(a)
32    return l
33
34def split(l, maxchunk):
35    """
36    Splits list l into chunks of size maxchunk. Each created chunk
37    except possibly the last one is of maximum allowed size.
38    """
39    sizes = splitN(len(l), maxchunk)
40    out = []
41    tillNow = 0
42    for s in sizes:
43        out.append(l[tillNow:tillNow+s])
44        tillNow += s
45    return out       
46
47def lloc(l,n):
48    """
49    List location in list of list structure.
50    Enable the use of negative locations:
51    -1 is the last element, -2 second last...
52    """
53    if n < 0:
54        return len(l[0])+n
55    else:
56        return n
57
58def loc(l,n):
59    """
60    List location.
61    Enable the use of negative locations:
62    -1 is the last element, -2 second last...
63    """
64    if n < 0:
65        return len(l)+n
66    else:
67        return n
68
69def nth(l,n):
70    """
71    Returns only nth elemnt in a list.
72    """
73    n = lloc(l,n)
74    return [ a[n] for a in l ]
75
76def imnth(l, ns):
77    """
78    Return only columns as specified in ns. Returns an generator.
79    """
80    ns = [ lloc(l,n) for n in ns ]
81    for a in l:
82        yield [ a[n] for n in ns ]
83
84def flatten(l,r=0):
85    """
86    Flatten a python structure into a list. Leave strings alone.
87    """
88    if type(l) == type("a"):
89        return [ l ]
90    try: #if enumerable then flatten it's elements
91        rec = [ flatten(a,r=r+1) for a in l ]
92        ret = []
93        for a in rec:
94            ret = ret + a
95        return ret
96    except:
97        return [ l ]
98
99def mxrange(lr):
100    """
101    Multiple xranges. Can be used to traverse matrices.
102    This function is very slow due to unknown number of
103    parameters.
104
105    >>> mxrange([3,5])
106    [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
107    >>> mxrange([[3,5,1],[9,0,-3]])
108    [(3, 9), (3, 6), (3, 3), (4, 9), (4, 6), (4, 3)]
109    """
110    if len(lr) == 0:
111        yield ()
112    else:
113        #it can work with single numbers
114        index = lr[0]
115        if type(1) == type(index):
116            index = [ index ]
117        for a in range(*index):
118            for b in mxrange(lr[1:]):
119                yield tuple([a] + list(b))
120
121
122def issequencens(x):
123    """
124    Is x a sequence and not string ? We say it is if it has a __getitem__
125    method and it is not an instance of basestring.
126    """
127    return hasattr(x, '__getitem__') and not isinstance(x, basestring)
128
129#end utility functions
130
131import statc
132#median = statc.median
133
134def median(l):
135    if len(l) == 0:
136        return None
137    else:
138        return statc.median(l)
139
140
141
142socket.setdefaulttimeout(60)
143
144verbose = 0
145
146class HttpGetException(Exception): pass
147
148def replaceChars(address):
149    return address.replace(" ", "%20")
150
151def httpGet(address, *args, **kwargs):
152    if verbose: 
153        print address, args, kwargs,  " "
154    address = replaceChars(address)
155    t1 = time.time()
156    f = urllib2.urlopen(address, *args, **kwargs)
157    read = f.read()
158    if verbose:
159        print "bytes", len(read),
160    if verbose:
161        print time.time() - t1
162    return read
163
164def txt2ll(s, separ=' ', lineSepar='\n'):
165    return [ a.split(separ) for a in s.split(lineSepar) ]
166
167class AuthenticationError(Exception):
168    pass
169
170class DBInterface(object):
171 
172    def __init__(self, address):
173        self.address = address
174
175    def raw(self, request, data=None, tryN=3):
176        if verbose:
177            print "tryN", tryN
178
179        def try_down():
180            try:
181                if data == None:
182                    return httpGet(self.address + request)
183                else:
184                    return httpGet(self.address + request, data=urllib.urlencode(data))
185            except urllib2.HTTPError, e:
186                if e.code == 403:
187                    raise AuthenticationError()
188                else:
189                    raise e
190
191        if tryN > 1:
192            try:
193                return try_down()
194            except IOError:
195                return self.raw(request, data=data, tryN=tryN-1)
196        else:
197            return try_down()
198
199    def get(self, request, data=None, tryN=3):
200        rawf = self.raw(request, data)
201        if rawf == None:
202            print "rawf == None!"
203            raise Exception("Connection error when contacting " + self.address + request)
204        if rawf.startswith("error: authentication failed"):
205            raise AuthenticationError()
206        elif rawf[:1] == "<" or rawf[:5] == "error" or rawf.startswith("MOD_PYTHON ERROR"): #an error occurred - starting some html input
207            #TODO are there any other kinds of errors?
208            if tryN > 0:
209                if verbose:
210                    print "trying again"
211                return self.get(request, data=data, tryN=tryN-1)
212            else:
213                if verbose:
214                    print rafw[:1000]
215                raise Exception("Error with the database")
216
217        a = txt2ll(rawf, separ='\t')
218       
219        if a[-1][0] == "": #remove empty line on end
220            a = a[:-1]
221        return a
222
223def _test():
224    import doctest
225    doctest.testmod()
226
227def splitTableOnColumn(ll,n):
228    omap = {}
229    n = lloc(ll,n)
230    for l in ll:
231        cell = omap.get(l[n], [])
232        cell.append(l)
233        omap[l[n]] = cell
234    return omap
235
236def neededColumns(legend, want):
237    return [ legend.index(a) for a in want ]
238
239def onlyColumns(ll, legend, want):
240    return list(imnth(ll, neededColumns(legend, want)))
241
242def ll2dic(ll, key=0, value=1):
243    """
244    Converts LL to map. Key is key position, value value position
245    """
246    names = nth(ll, lloc(ll, key))
247    if len(names) == len(set(names)):
248        return dict(list(imnth(ll, [ lloc(ll, key),  lloc(ll, value) ] )))
249    else:
250        raise Exception("all keys are not unique")
251
252def dic2ll(dic):
253
254    columns = sorted(dic.values()[0].keys())
255    ll = []
256    for key, d in dic.items():
257        ll.append([ key ] + [ d[a] for a in columns ])
258    return columns,ll
259
260def allUnique(els):
261    return len(els) == len(set(els))
262
263def reverseDic(d):
264    """
265    Create a reverse dictionary if a unique reverse is possible.
266    """
267    if allUnique(d.values()):
268        return dict([ (b,a) for a,b in d.items() ])
269
270def chainLookup(a, dics, force=[]):
271    """
272    Goes through a list of dictionaries. On each step try to
273    transform current key to value. The values becomes the key
274    for next search If unsuccessfull, query the next
275    dictionary with current key. Force last translation.
276    """
277    force = [ loc(dics, i) for i in force ]
278    for i,dic in enumerate(dics):
279        if i in force: #force specified translations
280            a = dic[a]
281        else:
282            if a in dic:
283                a = dic[a]
284    return a
285
286class DBCommon(object):
287
288    def fromBuffer(self, addr):
289        return self.buffer.get(self.address + addr)
290
291    def toBuffer(self, addr, cont, version, autocommit=True):
292        if self.buffer:
293            return self.buffer.add(self.address + addr, cont, version=version, autocommit=autocommit)
294
295    def bufferCommit(self):
296        if self.buffer:
297            self.buffer.commit()
298
299    def bufferFun(self, bufkey, bufver, reload, fn, *args, **kwargs):
300        """
301        If bufkey is already present in buffer, return its contents.
302        If not, run function with arguments and save its result
303        into the buffer.
304        """
305        if self.inBuffer(bufkey) == bufver and reload == False:
306            res = self.fromBuffer(bufkey)
307        else:
308            res = fn(*args, **kwargs)
309            self.toBuffer(bufkey, res, bufver)
310        return res
311
312    def sq(self, s1, data=None, buffer=True, bufadd="", bufname=None, bufver="0", reload=False, bufferkey=None, db=None):
313        if db == None:
314            db = self.db
315        if buffer:
316            bufkey = bufadd + (bufname if bufname != None else s1)
317            if bufferkey != None:
318                bufkey = bufferkey(bufkey, data)
319            res = self.bufferFun(bufkey, bufver, reload, db.get, s1, data=data)
320        else:
321            res = db.get(s1, data=data)
322        return res[1:],res[0]
323
324    def inBuffer(self, addr):
325        if self.buffer:
326            return self.buffer.contains(self.address + addr)
327        else:
328            return False
329
330    def dictionarize(self, ids, fn, *args, **kwargs):
331        """
332        Creates a dictionary from id: function result.
333        Callback for each part done.
334        """
335        callback = kwargs.pop("callback", None)
336        odic = {}
337        for a,b in izip(ids, fn(*args, **kwargs)):
338            odic[a] = b
339            if callback: callback()
340        return odic
341
342    def downloadMulti_bufcommand_replace_multi(self, command, data=None, chunk=100, bufferkey=None, transformfn=None):
343        """
344        Get function which gives buffer address for an id and a function
345        which replaces $MULTI$.
346        """
347
348        def bufferkey1(command, data):
349            if transformfn:
350                return "TRANS " + command
351            else:
352                return command
353
354        def replace_multi(command, data, repl):
355            return command.replace("$MULTI$", repl),\
356                dict((a,b.replace("$MULTI$", repl)) for a,b in sorted(data.items())) if data != None else None
357
358        if bufferkey == None:
359            bufferkey=bufferkey1
360
361        bufcommand = lambda x, c=command, d=data: bufferkey(*replace_multi(c, d, x))
362        return bufcommand, replace_multi
363
364    def downloadMulti(self, command, ids, data=None, chunk=100, transformfn=None, bufferkey=None, separatefn=None, bufreload=False, bufver="0"):
365        """
366        Downloads multiple results at once.
367        Results in the same order as in ids.
368
369        Bufferkey transforms command and data into buffer key.
370        bufver is a function returning buffer version for a given id. if
371            a string is given, use it for all ids
372        """
373
374        sids = split(ids,chunk)
375   
376        bufverfn = None
377        if isinstance(bufver, basestring):
378            bufverfn = lambda x: bufver
379        else:
380            bufverfn = bufver
381
382        bufcommand, replace_multi = self.downloadMulti_bufcommand_replace_multi(command, data=data, chunk=chunk, bufferkey=bufferkey, transformfn=transformfn)
383
384        for i,sidp in enumerate(sids):
385
386            buffered = []
387            unbuffered = []
388       
389            for a in sidp:
390                if self.inBuffer(bufcommand(a)) == bufverfn(a) and bufreload == False:
391                    buffered.append(a)
392                else:
393                    unbuffered.append(a)
394
395            res = []
396            legend = []
397
398            if len(unbuffered) > 0:
399                com1, d1 = replace_multi(command, data, ",".join(unbuffered))
400                res, legend = self.sq(com1, data=d1, buffer=False) #get unbuffered part
401            else:
402                # get legend from buffer also
403                legend = self.fromBuffer(bufcommand(buffered[0]))[0]
404
405            #split on different values of the first column - first attribute
406
407            if not separatefn:
408                antss = splitTableOnColumn(res, 0)
409            else:
410                legend, antss = separatefn([legend]+res)
411
412            #if transform before saving is requested, do it
413            if transformfn:
414                nantss = {}
415                nlegend = None
416                for a,b in antss.items():
417                    nb, nlegend = transformfn(b, legend)
418                    nantss[a] = nb
419                legend = nlegend
420                antss = nantss
421 
422            #here save buffer
423            for a,b in antss.items():
424                self.toBuffer(bufcommand(a), [ legend ] + b, bufverfn(a), autocommit=False)
425            self.bufferCommit()
426           
427
428            #get buffered from the buffer
429            antssb = dict([ (b, self.fromBuffer(bufcommand(b))[1:]) for b in buffered ])
430            antss.update(antssb)
431
432            #put results in order
433            tl = []
434            for ci in sidp:
435                yield antss[ci], legend
436
437    def exampleTables(self, ids, chipsm=None, spotmap={}, callback=None, exclude_constant_labels=False, annots={}, chipfn=None, allowed_labels=None):
438        """
439        Create example tables from chip readings, spot mappings and
440        group specifications.
441
442        group is the output from "sortAnnotations" function.
443        spotmap is a dictionary of { spotid: gene }
444        chipsm is a dictionary of chip readings
445
446        Callback: number of chipids + 2
447        """
448
449        if verbose:
450            print "Creating example table"
451
452        if callback: callback()
453
454        amap = {}
455        amapnext = 0
456
457        togen = []
458
459        groupnames = []
460        groupvals = []
461        groupannots = []
462
463        if chipsm == None:
464            chipdl = chipfn(ids)
465
466        for chipid in ids:
467
468            if chipsm != None:
469                chipdata = chipsm[chipid]
470            else:
471                chipdata = chipdl.next()
472
473            if callback: callback()
474
475            #add to current position mapping
476            repeats = {}
477            for id,_ in chipdata:
478                rep = repeats.get(id, 0)
479                repeats[id] = rep+1
480                key = (id, rep)
481                if key not in amap:
482                    amap[key] = amapnext
483                    amapnext += 1
484
485            vals = [ None ] * len(amap)
486
487            repeats = {}
488            for id,v in chipdata:
489                rep = repeats.get(id, 0)
490                repeats[id] = rep+1
491                key = (id, rep)
492                putind = amap[key]
493                vals[putind] = v
494            groupvals.append(vals)
495
496            groupnames.append(chipid) 
497
498            newannots = [['id', str(chipid)]] #add chipid to annotations
499            if annots:
500                newannots += annots[chipid]
501            groupannots.append(newannots)
502
503        togen = (groupnames, groupvals, groupannots)
504       
505        if callback: callback()
506
507        ddb = [ None ]*len(amap)
508        for (a,rep),pos in amap.items():
509            if len(spotmap):
510                ddb[pos] = spotmap.get(a, "#"+a)
511            else:
512                ddb[pos] = a
513
514        #this is sorted position mapping: key -> sortedind
515        posMap = dict( (k,i) for i,k in enumerate(sorted(amap.keys())) )
516        revmap = dict( ( (i,k) for k,i in amap.items() ) )
517        #permutation[i] holds target of current [i]
518        permutation = [ posMap[revmap[i]] for i in range(len(amap)) ]
519
520        def enlength(a, tlen):
521            """ Adds Nones to the end of the list """
522            if len(a) < tlen:
523                return a + [ "None" ]*(tlen-len(a))
524            else:
525                return a
526
527        def enlengthl(l, tlen):
528            return [ enlength(a, tlen) for a in l ]
529   
530        groupnames, groupvals, groupannots = togen
531
532        et = createExampleTable(groupnames, 
533            enlengthl(groupvals, len(ddb)),
534            groupannots, ddb, exclude_constant_labels=exclude_constant_labels, permutation=permutation, allowed_labels=allowed_labels)
535
536        if callback: callback()
537
538        return et
539
540def bufferkeypipa(command, data):
541    """ Do not save password to the buffer! """
542    command = command + " v5" #add version
543    if data != None:
544        data = data.copy()
545        if pipaparpass in data:
546            data.pop(pipaparpass)
547        return command + " " +  urllib.urlencode(sorted(data.items()))
548    else:
549        return command
550
551class PIPA(DBCommon):
552
553    def __init__(self, address=defaddresspipa, buffer=None, username=None, password=None):
554        self.address = address
555        self.db=DBInterface(address)
556        self.db_coverage=DBInterface(defaddresspipa_coverage) #temporary
557        self.buffer = buffer
558        self.username = None
559        if username != None:
560            self.username = username
561            self.password = password
562
563    def add_auth(self, data=None):
564        if self.username == None:
565            return data
566        authdic = { pipaparuser: self.username, pipaparpass: self.password }
567        if data != None:
568            authdic.update(data)
569        return authdic
570
571    def annotations(self, reload=False, bufver="0"):
572        """
573        Returns a dictionary of (id: dictionary of annotations).
574        """
575        #res, legend = self.sq("mapping_list", data=self.add_auth(), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
576        res, legend = self.sq("", data=self.add_auth({"action": "expression_list"}), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
577        return dict( (sa[0], dict(zip(legend[1:], sa[1:]))) for sa in res )
578
579    def chips(self, ids, ctype, reload=False, bufver="0"):
580        def separatefn(res):
581            #each one is own rown
582            #genes are in the first row
583            #remove unknown
584            genes = res[0][1:]
585            cids = nth(res,0)[1:]
586
587            antss = {}
588            for i,cid in enumerate(cids):
589                row = i+1
590                vals = res[row][1:]
591                antss[cid] = [ list(a) for a in zip(genes, vals) if a[1] != "?" ]
592            return ['gene_id', 'value'], antss
593
594        download_command = "gene_expression"
595        datadict = {"action": download_command, "ids":"$MULTI$", 'transpose':'1'}
596        if ctype != None:
597            datadict["type"] = ctype
598        antss = self.downloadMulti("", ids, data=self.add_auth(datadict), chunk=10, separatefn=separatefn, bufferkey=bufferkeypipa, bufreload=reload, bufver=bufver)
599        for a,legend in antss:
600            yield a
601
602    def coverage(self, id_, genome, chromosome, reload=False, bufver="0", raw=False):
603        data = self.add_auth({"action": "coverage_strand", "chr_name": str(chromosome), 
604            "pixel_size": "1", "genome": genome, "sample_id": str(id_), "raw": str(raw).lower()})
605
606        def comp():
607
608            d = dict(data)
609
610            def get():
611                #sq returns [ "all lines after first", "firstline"] - take first column!
612                #do not use buffering on this stage!
613                a = self.sq("", data=d, reload=reload, buffer=False, bufferkey=bufferkeypipa, bufver=bufver, db=self.db_coverage)[1][0].strip("[]")
614                return map(int, a.split(", "))
615               
616            d["strand"] = "plus"
617            plus = get()
618            d["strand"] = "minus"
619            minus = get()
620
621            import numpy
622            return numpy.array([plus, minus])
623       
624        bufkey =  bufferkeypipa("", data)
625        return self.bufferFun(bufkey, bufver, reload, comp)
626
627
628    def gene_expression_types(self, reload=False, bufver="0"):
629        #res, legend = self.sq("gene_expression_type", data={self.add_auth(), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
630        res, legend = self.sq("", data=self.add_auth({"action":"gene_expression_type"}), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
631        return sorted(tuple(a) for a in res)
632
633    def chips_keynaming(self, ctype):
634        keynamingfn,_ = self.downloadMulti_bufcommand_replace_multi("", data=self.add_auth({"action": "gene_expression", "ids":"$MULTI$", 'transpose':'1', 'type':ctype}), chunk=100, bufferkey=bufferkeypipa, transformfn=None)
635        return keynamingfn
636
637    def get_data(self, exclude_constant_labels=False, average=median, 
638        ids=None, callback=None, bufver="0", transform=None, ctype=None, allowed_labels=None):
639        """
640        Get data in a single example table with labels of individual attributes
641        set to annotations for query and post-processing
642        instructions.
643
644        Parameters:
645            average: function used for combining multiple reading of the same spot on
646                a chip. If None, no averaging is done. Fuction should take a list
647                of floats and return an "averaged" float.
648            ids: a list of chip ids. If absent, make a search
649            exclude_constant_labels: if a label has the same value in whole
650                example table, remove it
651            format: if short, use short format for chip download
652            ctype: expression type, from gene_expression_types
653
654        Defaults: Median averaging.
655        """
656
657        def optcb():
658            if callback: callback()
659
660        cbc = CallBack(1, optcb, callbacks=10)
661
662        if not ids:
663            return None
664
665        cbc.end()
666
667        #downloads annotations
668        cbc = CallBack(len(ids), optcb, callbacks=10)
669
670        readall = self.annotations()
671
672        read = {}
673        for a,b in readall.items():
674            read[a] = b.items()
675
676        cbc.end()
677
678        #till now downloads were small
679
680        import time
681        tstart = time.time()
682
683        #here download actually happens
684        chipfn = None
685
686        chipfn = lambda x: self.chips(x, ctype, bufver=bufver)
687       
688        if verbose:
689            print "DOWNLOAD TIME", time.time() - tstart
690
691        cbc = CallBack(len(ids)*2+len(ids)+1, optcb, callbacks=999-30)
692        et = self.exampleTables(ids, spotmap={}, callback=cbc, annots=read, exclude_constant_labels=exclude_constant_labels, chipfn=chipfn, allowed_labels=allowed_labels)
693        cbc.end()
694
695        cbc = CallBack(1, optcb, callbacks=10)
696
697        #transformation is performed prior to averaging
698        if transform != None:
699            transformValues(et, fn=transform) #in place transform
700            cbc()
701
702        #if average function is given, use it to join same spotids
703        if average != None:
704            et = averageAttributes(et, fn=average)
705            cbc()
706
707        cbc.end()
708
709        return et
710
711
712class PIPAx(PIPA):
713    """`PIPAx <http://pipa.biolab.si/?page_id=23>` api interface.
714    """
715
716    API_ADDRESS = "https://pipa.biolab.si/pipax/api.py"
717
718    def __init__(self, address=API_ADDRESS, buffer=None,
719                 username=None, password=None):
720        self.address = address
721        self.db = DBInterface(address)
722        self.buffer = buffer
723        self.username = username
724        self.password = password
725
726    def genomes(self, reload=False, bufver="0"):
727        """Return a list of available genomes as a list of
728        (genome_id, genome_name) tuples.
729
730        >>> pipax.genomes()
731        [('dd', 'Dictyostelium discoideum'), ('dp', 'Dictyostelium purpureum')]
732
733        """
734        data = {"action": "genomes"}
735        res, _ = self.sq("", data=data, reload=reload, bufver=bufver)
736        return [tuple(r) for r in res]
737
738    def mappings(self, reload=False, bufver="0"):
739        """Return a dictionary of {unique_id: dictionary_of_annotations}
740        where the keys for dictionary_of_annotations are
741        ["id", data_id", "data_name", "genomes_id"]
742
743        >>> mappings = pipax.mappings()
744
745        """
746        data = self.add_auth({"action": "mappings"})
747        res, legend = self.sq("", data=data, reload=reload,
748                              bufferkey=bufferkeypipa,
749                              bufver=bufver)
750
751        return dict((sa[0], dict(zip(legend[1:], sa[1:]))) for sa in res)
752
753    def result_types(self, reload=False, bufver="0"):
754        """Return a list of available result type templates.
755        """
756        data = {"action": "results_templates"}
757        res, _ = self.sq("", data=data, reload=reload, bufver=bufver)
758        return sorted(tuple(a) for a in res)
759
760    def results_list(self, rtype, reload=False, bufver="0"):
761        """Return a list of available results for a result type template
762        `rtype` (a key from the `result_types` return value).
763
764        """
765        data = {"action": "results",
766                "results_templates_id": rtype}
767        data = self.add_auth(data)
768        res, legend = self.sq("", data=data, reload=reload,
769                              bufferkey=bufferkeypipa,
770                              bufver=bufver)
771        # index by unique_id (last column)
772        res = splitTableOnColumn(res, -1)
773        return dict((id, dict(zip(legend, line[0]))) \
774                    for id, line in res.items())
775
776    def download_key_function(self):
777        data = self.add_auth({"action": "download",
778                              "ids": "$MULTI$"}
779                                )
780        keynamingfn, _ = self.downloadMulti_bufcommand_replace_multi("",
781                         data=data, chunk=100, bufferkey=bufferkeypipa,
782                         transformfn=None)
783        return keynamingfn
784
785    def get_data(self, ids=None, result_type=None,
786                 exclude_constant_labels=False, average=median,
787                 callback=None, bufver="0", transform=None,
788                 allowed_labels=None):
789        """
790        Get data in a single example table with labels of individual
791        attributes set to annotations for query and post-processing
792        instructions.
793
794        :param ids: If `result_type` is specified then this must
795            be a list of  `unique_id`s or (id, data_id) tuples as
796            returned by `mappings`. Else the `ids` must be a list
797            of `unique_id`s as returned by `results_list`.
798        :type ids: list
799
800        :param result_type: Result template type id as returned by
801             `result_types`. Not specified by default (see `ids`.)
802        :type result_type: str
803
804        :param exclude_constant_labels: If a label has the same value
805            in whole example table, remove it.
806        :type exclude_constant_labels: bool
807
808        :param average: Function used for combining multiple reading of
809            the same spot on a chip. If None, no averaging is done.
810            Function should take a list of floats and return an "averaged"
811            float (default `median`).
812        :type average: function
813
814        """
815
816        def optcb():
817            if callback:
818                callback()
819
820        cbc = CallBack(len(ids), optcb, callbacks=10)
821
822        if result_type is not None:
823            res_list = self.results_list(result_type, reload=reload,
824                                         bufver=bufver)
825            # Map (data_id, mapping_id) to unique_id
826            res_types_to_unique_id = \
827                dict(((annot["data_id"], annot["mappings_id"]),
828                      annot["unique_id"]) \
829                     for annot in res_list.values())
830
831            mappings = self.mappings(reload=reload, bufver=bufver)
832
833            def id_map(mappings_unique_id):
834                cbc()
835                if isinstance(mappings_unique_id, tuple):
836                    data_id, mappings_id = mappings_unique_id
837                else:
838                    annot = mappings[mappings_unique_id]
839                    data_id = annot["data_id"]
840                    mappings_id = annot["id"]
841                return res_types_to_unique_id[data_id, mappings_id]
842
843            ids = map(id_map, ids)
844        else:
845            result_type_set = set(id.rsplit("_", 1)[-1] for id in ids)
846            if len(result_type_set) != 1:
847                raise ValueError("""\
848Can only retrieve a single result_template_type at a time"""
849)
850            result_type = result_type_set.pop()
851            res_list = self.results_list(result_type, reload=reload,
852                                         bufver=bufver)
853
854        read = {}
855        for a, b in res_list.items():
856            read[a] = b.items()
857
858        cbc.end()
859
860        download_func = lambda x: self.download(x, reload=reload,
861                                                bufver=bufver)
862
863        cbc = CallBack(len(ids) + 3, optcb,
864                       callbacks=99 - 20)
865        et = self.exampleTables(ids, spotmap={}, callback=cbc, annots=read,
866                            exclude_constant_labels=exclude_constant_labels,
867                            chipfn=download_func,
868                            allowed_labels=allowed_labels)
869        cbc.end()
870
871        cbc = CallBack(2, optcb, callbacks=10)
872
873        #transformation is performed prior to averaging
874        if transform != None:
875            transformValues(et, fn=transform)  # in place transform
876            cbc()
877
878        #if average function is given, use it to join same spotids
879        if average != None:
880            et = averageAttributes(et, fn=average)
881            cbc()
882
883        cbc.end()
884
885        return et
886
887    def download(self, result_ids, reload=False, bufver="0"):
888        data = {"action": "download", "ids": "$MULTI$"}
889        data = self.add_auth(data)
890        antss = self.downloadMulti("", result_ids, data=data, chunk=10,
891                                   bufferkey=bufferkeypipa, bufreload=reload,
892                                   bufver=bufver)
893        for a, legend in antss:
894            yield a
895
896    def downloadMulti(self, command, ids, data=None, chunk=100,
897                      transformfn=None, bufferkey=None, separatefn=None,
898                      bufreload=False, bufver="0"):
899        """
900        Downloads multiple results at once.
901        Results in the same order as in ids.
902
903        Bufferkey transforms command and data into buffer key.
904        bufver is a function returning buffer version for a given id. if
905            a string is given, use it for all ids
906        """
907
908        sids = split(ids, chunk)
909
910        bufverfn = None
911        if isinstance(bufver, basestring):
912            bufverfn = lambda x: bufver
913        else:
914            bufverfn = bufver
915
916        bufcommand, replace_multi = \
917                self.downloadMulti_bufcommand_replace_multi(command,
918                                data=data, chunk=chunk, bufferkey=bufferkey,
919                                transformfn=transformfn
920                                )
921
922        for i, sidp in enumerate(sids):
923
924            buffered = []
925            unbuffered = []
926
927            for a in sidp:
928                if self.inBuffer(bufcommand(a)) == bufverfn(a) and \
929                        bufreload == False:
930                    buffered.append(a)
931                else:
932                    unbuffered.append(a)
933
934            res = []
935            legend = []
936
937            if len(unbuffered) > 0:
938                com1, d1 = replace_multi(command, data, ",".join(unbuffered))
939                # get unbuffered part
940                res, legend = self.sq(com1, data=d1, buffer=False)
941            else:
942                # get legend from buffer also
943                legend = self.fromBuffer(bufcommand(buffered[0]))[0]
944
945            legend = ["gene_id", "value"]
946            genes = nth(res, 0)[1:]
947
948            antss = {}
949            for i, cid in enumerate(unbuffered):
950                col = i + 1
951                vals = nth(res, col)
952                antss[cid] = [[a, b] for a, b in zip(genes, vals) if b != "?"]
953
954            #here save buffer
955            for a, b in antss.items():
956                self.toBuffer(bufcommand(a), [legend] + b, bufverfn(a),
957                              autocommit=False)
958            self.bufferCommit()
959
960            #get buffered from the buffer
961            antssb = dict([(b, self.fromBuffer(bufcommand(b))[1:]) \
962                           for b in buffered])
963            antss.update(antssb)
964
965            #put results in order
966            for ci in sidp:
967                yield antss[ci], legend
968
969
970class DictyExpress(DBCommon):
971    """
972    Type is object id
973    """
974   
975    aoidPairs = txt2ll("""time extractions.developmental_time_point
976sample biological_samples.sample
977growthCond biological_samples.growth_condition
978treatment biological_samples.treatment
979replicate biological_sample_replicates.replicate
980techReplicate chips.replicate
981platform chips.chip_platform
982isTimeSeries biological_sample_replicates.is_time_series""")
983
984    obidPairs = txt2ll("""norms normalizations
985samples biological_samples
986replicates biological_sample_replicates
987analysis analysis
988experiments experiments
989extractions extractions
990chips chips""")
991
992    def __init__(self, address=defaddress, buffer=None):
993        self.address = address
994        self.db = DBInterface(address)
995        self.buffer = buffer
996        self.preload()
997
998    def preload(self):
999
1000        # aoids are mappings from annotation name to annotation id
1001        self.aoids = ll2dic(self.__annotationTypes(), 1, 0)
1002        self.saoids = ll2dic(self.aoidPairs, 0, 1)
1003        self.aoidsr = reverseDic(self.aoids)
1004        self.saoidsr = reverseDic(self.saoids)
1005
1006        # obids are mappings from object id to annotation id
1007        self.obids = ll2dic(self.__objects(), 1, 0)
1008        self.sobids = ll2dic(self.obidPairs, 0, 1)
1009        self.obidsr = reverseDic(self.obids)
1010        self.sobidsr = reverseDic(self.sobids)
1011
1012    def aoidt(self, s):
1013        return chainLookup(s, [self.saoids, self.aoids], force=[-1])
1014
1015    def obidt(self, s):
1016        return chainLookup(s, [self.sobids, self.obids], force=[-1])
1017 
1018    def aoidtr(self, s, **kwargs):
1019        return chainLookup(s, [self.aoidsr, self.saoidsr], **kwargs)
1020
1021    def obidtr(self, s):
1022        return chainLookup(s, [self.obidsr, self.sobidsr])
1023
1024    def pq(self, q):
1025        """
1026        Prepare query.
1027        ||| separator between conditions,
1028        *** denotes equivalence
1029        """
1030        o =  "|||".join([ self.aoidt(a) + "***" + b for a,b in q.items()])
1031        return o
1032
1033    def geneInfo(self):
1034        res,legend = self.sq("action=gene_info")
1035        return res, legend
1036
1037    def annotationOptions(self, ao=None, onlyDiff=False, **kwargs):
1038        """
1039        Returns annotation options for given query. Returns all possible
1040        annotations if the query is omitted.
1041
1042        If ao is choosen, only result
1043        """
1044        params = ""
1045        if len(kwargs) > 0: params += "&query=" + self.pq(kwargs)
1046        if ao: params += "&annotation_object_id=" +  self.aoidt(ao)
1047        res,legend = self.sq("action=get_annotation_options%s" % (params), bufadd=self.address)
1048        res = onlyColumns(res, legend, ['annotation_object_id', 'value' ])
1049
1050        #join columns with the annotation object id
1051        joined = {}
1052        for key,v in res:
1053            key = self.aoidtr(key)
1054            cur = joined.get(key, [])
1055            cur.append(v)
1056            joined[key] = cur
1057
1058        if onlyDiff:
1059            joined = dict([ (a,b) for a,b in joined.items() if len(b)>1 ])
1060
1061        return dict([ (a, sorted(b)) for a,b in joined.items() ])
1062
1063    def annotation(self, type, id):
1064        return list(self.annotations(type, [ id ]))[0]
1065
1066    def meaningfulAnnot(self, name):
1067        if name in self.saoids:
1068            return True
1069        else:
1070            return False
1071
1072    def keepOnlyMeaningful(self, annot):
1073        """
1074        Keep only meaningful annotations
1075        """
1076        if type(annot) == type({}):
1077            return dict( [ (a,b) for a,b in annot.items() \
1078                if self.meaningfulAnnot(a) ] )
1079        else:
1080            return [ [ a,b ] for a,b in annot \
1081                if self.meaningfulAnnot(a) ]
1082
1083
1084    def annotations(self, type, ids=None, all=False):
1085        """
1086        Returns a generator returning annotations for specified type and ids.
1087        If ids are left blank, all annotations are outputed. Annotations are in the same order
1088        as input ids.
1089        If all is True, all annotations are kept, else keep only "meaningful".
1090        """
1091       
1092        inputids = False
1093        if ids != None:
1094            inputids = True
1095            antss = self.downloadMulti(
1096                "action=get_annotations&ids=$MULTI$&object_id=%s" 
1097                % (self.obidt(type)), ids)
1098        else:
1099            res,legend = self.sq(
1100                "action=get_annotations&object_id=%s"
1101                % (self.obidt(type)))
1102            antss = splitTableOnColumn(res, 0)
1103            ids = nth(antss.items(),0)
1104            antss = zip(nth(antss.items(),1), [ legend ]*len(antss))
1105
1106        for ants in izip(antss,ids):
1107            (res, legend), id = ants
1108            res2 = onlyColumns(res, legend, ['name', 'value'])
1109            res2 = [ [ self.aoidtr(a),b ] for a,b in res2 ]
1110            if not all:
1111                res2 = self.keepOnlyMeaningful(res2)
1112            if inputids:
1113                yield res2
1114            else:
1115                yield (id, res2)
1116
1117    def search(self, type, **kwargs):
1118        """
1119        Break search for multiple values of one attribute to independant searches.
1120        Search is case insensitive.
1121       
1122        List of searchable annotation types: self.saoids.keys()
1123
1124        example usage:
1125        search("norms", platform='minichip', sample='abcC3-')
1126            finds all ids of normalized entries where platform is minchip and
1127            sample is abcC3-
1128        search("norms", platform='minichip', sample=[ 'abcC3-', 'abcG15-'])
1129            finds all ids of normalized entries where platform is minichip and
1130            sample is abcC3- or those where platform is minichip and sample
1131            is abcG15-
1132        """
1133       
1134        #search for all combinations of values - this is slow!
1135
1136        l = []
1137        for k,v in kwargs.items():
1138            if not issequencens(v):
1139                v = [ v ]
1140            l.append((k,v))
1141
1142        ares = []
1143
1144        for r in mxrange([ len(v) for k,v in l ]):
1145            dico = {}
1146            for i,a in enumerate(r):
1147                dico[l[i][0]] = l[i][1][a]
1148
1149            res,_ = self.sq("action=search&object_id=%s&query=%s" \
1150                % (self.obidt(type), self.pq(dico)), bufadd=self.address)
1151
1152            ares += res
1153
1154        return sorted(set(nth(ares, 0)))
1155
1156
1157    def chipN(self, id):
1158        return list(self.chipNs([id]))[0]
1159
1160    def chipR(self, id):
1161        return list(self.chipRs([id]))[0]
1162 
1163    def chipNs(self, ids, remove_bad=True):
1164        """
1165        If removebad = True removes those with weights 0.
1166        """
1167         
1168        def sel(res, legend):
1169            #Drop unwanted columns - for efficiency
1170            res = onlyColumns(res, legend, ["spot_id", 'M', 'weights'])
1171            legend = onlyColumns( [ legend ], legend, ["spot_id", 'M', 'weights'])[0]
1172
1173            def transf(a):
1174                if a[2] == 0:
1175                    return a[0], '', a[2]
1176                else:
1177                    return a[0], a[1], a[2]
1178
1179            res = [ transf(a) for a in res ]
1180            res = onlyColumns(res, legend, ["spot_id", 'M'])
1181            legend = onlyColumns( [ legend ], legend, ["spot_id", 'M'])[0]
1182            return res, legend
1183
1184        antss = self.downloadMulti("action=get_normalized_data&ids=$MULTI$", ids, chunk=2, transformfn=sel)
1185        for a,legend in antss:
1186            yield a   
1187
1188    def chipNsN(self, ids, annots):
1189        """
1190        Download chips using new shorter format.
1191        """
1192        chip_map_ids = zip(ids,[ dict(a)['chips.chip_map_id'] for a in annots ])
1193
1194        def separateByChipsMaps(l):
1195            begin = 0
1196            cm = l[0][1]
1197            cp = 0
1198            for id,m in l[1:]:
1199                cp += 1
1200                if m != cm:
1201                    yield l[begin:cp]
1202                    cm = m
1203                    begin = cp
1204            yield l[begin:cp+1]
1205       
1206        sep = list(separateByChipsMaps(chip_map_ids))
1207     
1208        def sel(res, legend):
1209            #Drop unwanted columns - for efficiency
1210            res = onlyColumns(res, legend, ["spot_id", 'M'])
1211            legend = onlyColumns( [ legend ], legend, ["spot_id", 'M'])[0]
1212            return res, legend
1213
1214        def separatefn(res):
1215            #each one is own rown
1216            #genes are in the first row
1217            genes = res[0][1:]
1218            cids = nth(res,0)[1:]
1219
1220            antss = {}
1221            for i,cid in enumerate(cids):
1222                row = i+1
1223                vals = res[row][1:]
1224                antss[cid] = [ list(a) for a in zip(genes, vals) ]
1225            return ['spot_id', 'M'], antss
1226
1227        for part in sep:
1228            pids = nth(part,0)
1229            antss = self.downloadMulti("action=get_normalized_data&mergeexperiments=1&ids=$MULTI$", pids, chunk=10, transformfn=sel, separatefn=separatefn)
1230            for a, legend in antss:
1231                yield a
1232
1233    def chipRs(self, id):
1234        antss = self.downloadMulti("action=get_raw_data&ids=$MULTI$", ids, chunk=2)
1235        for a,legend in antss:
1236            yield a
1237 
1238    def spotId(self):
1239        res,legend = self.sq("action=spot_id_mapping")
1240        res2 = onlyColumns(res, legend, ["spot_id", 'ddb_g', 'genename'])
1241        return res2
1242
1243    def annotationTypes(self):
1244        """
1245        Returns all annotation types.
1246        """
1247        return self.aoids.keys()
1248
1249    def objects(self):
1250        """
1251        Returns all objects.
1252        """
1253        return self.obids.keys()
1254
1255    def __annotationTypes(self):
1256        """
1257        Returns list of [ annotation_object_id, name ]
1258        """
1259        res, legend = self.sq("action=get_annotation_types")
1260        res2 = onlyColumns(res, legend, ["annotation_object_id", 'name'])
1261        return res2
1262
1263    def __objects(self):
1264        res, legend = self.sq("action=get_objects")
1265        res2 = onlyColumns(res, legend, ["object_id", 'object_name'])
1266        return res2
1267
1268    def spotMap(self):
1269        spotids = self.spotId()
1270        spotmap = [ (a[0],a[1]) for a in spotids ]
1271
1272        spotmapd = {}
1273
1274        for a,b in spotmap:
1275            if a in spotmapd:
1276                spotmapd[a] = spotmapd[a] + "-" + b
1277            else:
1278                spotmapd[a] = b
1279
1280        return spotmapd
1281
1282    def getData(self, *args, **kwargs):
1283        deprecatedError("Use get_single_data instead")
1284
1285    def get_data(self, type="norms", exclude_constant_labels=False, average=median, 
1286        ids=None, callback=None, format="short", transform=None, allowed_labels=None, **kwargs):
1287        """
1288        Get data in a single example table with labels of individual attributes
1289        set to annotations for query and post-processing
1290        instructions.
1291
1292        Parameters:
1293            average: function used for combining multiple reading of the same spot on
1294                a chip. If None, no averaging is done. Fuction should take a list
1295                of floats and return an "averaged" float.
1296            ids: a list of chip ids. If absent, make a search
1297            exclude_constant_labels: if a label has the same value in whole
1298                example table, remove it
1299            format: if short, use short format for chip download
1300
1301        Defaults: Median averaging.
1302        """
1303
1304        def optcb():
1305            if callback: callback()
1306
1307        cbc = CallBack(1, optcb, callbacks=10)
1308
1309        if not ids:
1310            #returns ids of elements that match the search function
1311            ids = self.search(type, **kwargs)
1312
1313        cbc.end()
1314
1315        #downloads annotations
1316        cbc = CallBack(len(ids), optcb, callbacks=10)
1317
1318        readall = self.dictionarize(ids, self.annotations, type, ids, all=True, callback=cbc)
1319
1320        read = {}
1321        for a,b in readall.items():
1322            read[a] = self.keepOnlyMeaningful(b)
1323
1324        annotsinlist = [] #annotations in the same order
1325        for id in ids:
1326            annotsinlist.append(readall[id])
1327
1328        if verbose:
1329            print zip(ids,[ dict(a)['chips.chip_map_id'] for a in annotsinlist ])
1330
1331        cbc.end()
1332
1333        #till now downloads were small
1334
1335        import time
1336        tstart = time.time()
1337
1338        #here download actually happens
1339        chipfn = None
1340
1341        if type == "norms":
1342            if format == "short":
1343                chipfn = lambda x, al=annotsinlist: self.chipNsN(x, al)
1344            else:
1345                chipfn = self.chipNs
1346        else:
1347            chipfn = self.chipRs
1348       
1349        if verbose:
1350            print "DOWNLOAD TIME", time.time() - tstart
1351
1352        cbc = CallBack(len(ids)*2+len(ids)+1, optcb, callbacks=999-30)
1353        et = self.exampleTables(ids, spotmap=self.spotMap(), callback=cbc, annots=read, exclude_constant_labels=exclude_constant_labels, chipfn=chipfn, allowed_labels=allowed_labels)
1354        cbc.end()
1355
1356        cbc = CallBack(1, optcb, callbacks=10)
1357
1358        #transformation is performed prior to averaging
1359        if transform != None:
1360            transformValues(et, fn=transform) #in place transform
1361            cbc()
1362
1363        #if average function is given, use it to join same spotids
1364        if average != None:
1365            et = averageAttributes(et, fn=average)
1366            cbc()
1367
1368        cbc.end()
1369
1370        return et
1371   
1372    def get_single_data(self, *args, **kwargs):
1373        return self.get_data(*args, **kwargs)
1374
1375class DatabaseConnection(DictyExpress):
1376    pass
1377
1378def allAnnotationVals(annots):
1379    """
1380    All annotation valuess for given annotations
1381    in a dict of { name: set of possible values } pairs.
1382    """
1383    av = defaultdict(set)
1384    for a in annots:
1385        for name,val in a:
1386            av[name].add(val)
1387    return av
1388
1389def createExampleTable(names, vals, annots, ddb, cname="DDB", \
1390        exclude_constant_labels=False, permutation=None, always_include=["id"], allowed_labels=None):
1391    """
1392    Create an ExampleTable for this group. Attributes are those in
1393    names.
1394    """
1395    attributes = [ orange.FloatVariable(n, numberOfDecimals=3) \
1396        for n in names ]
1397
1398    #exclusion of names with constant values
1399    annotsvals = allAnnotationVals(annots)
1400    oknames = set(annotsvals.keys())
1401    if exclude_constant_labels:
1402        oknames = set(nth(filter(lambda x: len(x[1]) > 1 or x[0] in always_include, 
1403            annotsvals.items()), 0))
1404
1405    if allowed_labels != None:
1406        oknames = set(filter(lambda x: x in allowed_labels, oknames))
1407
1408    #print oknames
1409
1410    for a,an in zip(attributes, annots):
1411        a.setattr("attributes", dict([(name,str(val)) for name,val in an if name in oknames]))
1412
1413    domain = orange.Domain(attributes, False)
1414    ddbv = orange.StringVariable(cname)
1415    id = orange.newmetaid()
1416    domain.addmeta(id, ddbv)
1417
1418    examples = [ None ]*len(ddb)
1419    for i,(v,d) in enumerate(izip(izip(*vals), ddb)):
1420        ex = orange.Example(domain, [ floatOrUnknown(a) for a in v ])
1421        ex[cname] = d
1422
1423        if permutation:
1424            examples[permutation[i]] = ex
1425        else:
1426            examples[i] = ex
1427
1428    return orange.ExampleTable(domain,examples)
1429
1430def transformValues(data, fn):
1431    """
1432    In place transformation.
1433    """
1434    for ex in data:
1435        for at in data.domain.attributes:
1436            if not ex[at].isSpecial():
1437                ex[at] = fn(ex[at])
1438
1439def averageAttributes(data, joinc="DDB", fn=median):
1440    """
1441    Averages attributes with the same "join" parameter using
1442    specified function. Builds a now dataset. Order of
1443    "join" parameter stays the same with regard to first
1444    appearance.
1445    """
1446
1447    if verbose:
1448        print "Averaging attributes"
1449
1450    valueso = []
1451    valuess = set(valueso)
1452
1453    attributes = [ a for a in data.domain.attributes ]
1454    domain = data.domain
1455   
1456    #accumulate values
1457
1458    for ex in data:
1459        j = str(ex[joinc])
1460
1461        if j not in valuess:
1462            valueso.append(j)
1463            valuess.add(j)
1464
1465    valuesddb = [ dict( (n,[]) for n in valueso) for at in attributes ]
1466   
1467    for ex in data:
1468        j = str(ex[joinc])
1469
1470        for a in range(len(attributes)):
1471            val = ex[a]
1472            if not val.isSpecial():
1473                valuesddb[a][j].append(val.native())
1474
1475    #create a new example table reusing the domain - apply fn
1476    examples = []
1477    for v in valueso:
1478        example = orange.Example(domain, \
1479            [ floatOrUnknown(fn(valuesddb[a][v])) for a in range(len(attributes)) ] )
1480        example[joinc] = v
1481        examples.append(example)
1482
1483    return orange.ExampleTable(domain, examples)
1484
1485def floatOrUnknown(a):
1486    """
1487    Converts an element to float if possible.
1488    If now, output "?".
1489    """
1490    try:
1491        return float(a)
1492    except:
1493        return "?"
1494
1495class CallBack():
1496    """
1497    Converts "allparts" callbacks into by "callbacks"
1498    specified number of callbacks of function fn.
1499    """
1500
1501    def __init__(self, allparts, fn, callbacks=100):
1502        self.allparts = allparts
1503        self.lastreport = 0.00001
1504        self.getparts = 0
1505        self.increase = 1.0/callbacks
1506        self.callbacks = callbacks
1507        self.fn = fn
1508        self.cbs = 0
1509
1510    def __call__(self):
1511        self.getparts += 1
1512        done = float(self.getparts)/self.allparts
1513        while done > self.lastreport + self.increase:
1514            self.lastreport += self.increase
1515            self.fn()
1516            self.cbs += 1
1517
1518    def end(self):
1519        while self.cbs < self.callbacks:
1520            self.fn()
1521            self.cbs += 1
1522
1523class BufferSQLite(object):
1524
1525    def __init__(self, filename, compress=True):
1526        self.compress = compress
1527        self.filename = filename
1528        self.conn = self.connect()
1529
1530    def clear(self):
1531        """
1532        Removes all entries in the buffer
1533        """
1534        self.conn.close()
1535        os.remove(self.filename)
1536        self.conn = self.connect()
1537
1538    def connect(self):
1539        import sqlite3
1540        conn = sqlite3.connect(self.filename)
1541        c = conn.cursor()
1542        c.execute('''create table if not exists buf
1543        (address text primary key, time text, con blob)''')
1544        c.close()
1545        conn.commit()
1546        return conn
1547
1548    def contains(self, addr):
1549        """ Returns version or False, if it does not exists """
1550        c = self.conn.cursor()
1551        c.execute('select time from buf where address=?', (addr,))
1552        lc = list(c)
1553        c.close()
1554        if len(lc) == 0:
1555            return False
1556        else:
1557            return lc[0][0]
1558
1559    def list(self):
1560        c = self.conn.cursor()
1561        c.execute('select address from buf')
1562        return nth(list(c), 0)
1563
1564    def add(self, addr, con, version="0", autocommit=True):
1565        import cPickle, zlib, sqlite3
1566        if verbose:
1567            print "Adding", addr
1568        c = self.conn.cursor()
1569        if self.compress:
1570            bin = sqlite3.Binary(zlib.compress(cPickle.dumps(con)))
1571        else:
1572            bin = sqlite3.Binary(cPickle.dumps(con))
1573        c.execute('insert or replace into buf values (?,?,?)', (addr, version, bin))
1574        c.close()
1575        if autocommit:
1576            self.commit()
1577
1578    def commit(self):
1579        self.conn.commit()
1580
1581    def get(self, addr):
1582        import cPickle, zlib
1583        if verbose:
1584            print "getting from buffer", addr
1585            t = time.time()
1586        c = self.conn.cursor()
1587        c.execute('select con from buf where address=?', (addr,))
1588        ls = list(c)
1589        first = ls[0][0]
1590        if verbose:
1591            print time.time() - t
1592        if self.compress:
1593            rc = cPickle.loads(zlib.decompress(first))
1594        else:
1595            rc = cPickle.loads(str(first))
1596        c.close()
1597
1598        if verbose:
1599            print time.time() - t
1600        return rc
1601
1602def download_url(url, repeat=2):
1603    def do():
1604        return urllib2.urlopen(url)
1605
1606    if repeat <= 0:
1607        do()
1608    else:
1609        try:
1610            return do()
1611        except:
1612            return download_url(url, repeat=repeat-1)
1613
1614def empty_none(s):
1615    if s:
1616        return s
1617    else:
1618        return None
1619
1620def join_ats(atts):
1621    """ Joins attribute attributes together. If all values are the same,
1622    set the parameter to the common value, else return a list of the
1623    values in the same order as the attributes are imputed. """
1624    keys = reduce(lambda x,y: x | y, (set(at.keys()) for at in atts))
1625    od = {}
1626
1627    def man(x):
1628        if issequencens(x):
1629            return tuple(x)
1630        else:
1631            return x
1632
1633    for k in keys:
1634        s = set(man(at[k]) for at in atts)
1635        if len(s) == 1:
1636            od[k] = list(s)[0]
1637        else:
1638            od[k] = str([ at[k] for at in atts ])
1639    return od
1640
1641def join_replicates(data, ignorenames=["replicate", "id", "name", "map_stop1"], namefn=None, avg=median):
1642    """ Join replicates by median.
1643    Default parameters work for PIPA data.
1644    Sort elements in the same order as ignorenames!
1645    """
1646
1647    d = defaultdict(list)
1648
1649    if namefn == None:
1650        namefn = lambda att: ",".join(att["id"]) if issequencens(att["id"]) else att["id"]
1651
1652    #key function
1653    def key_g(att):
1654        dk = att.copy()
1655        for iname in ignorenames:
1656            dk.pop(iname, None)
1657       
1658        def man(x):
1659            if issequencens(x):
1660                return tuple(x)
1661            else:
1662                return x
1663
1664        return tuple(nth(sorted(((a, man(b)) for a,b in dk.items())), 1))
1665
1666    #prepare groups
1667    for i,a in enumerate(data.domain.attributes):
1668        att = a.attributes
1669        k = key_g(att)
1670        d[k].append(i)
1671
1672    d = dict(d) #want errors with wrong keys
1673
1674    natts = []
1675
1676    def nativeOrNone(val):
1677        if val.isSpecial(): 
1678            return None
1679        else: 
1680            return val.native()
1681
1682    def avgnone(l):
1683        """ Removes None and run avg function"""
1684        l = filter(lambda x: x != None, l)
1685        if len(l):
1686            return avg(l)
1687        else:
1688            return None
1689
1690    def data_type(vals): #data type converter
1691        try:
1692            _ = [ int(a) for a in vals ]
1693            return int
1694        except:
1695            try:
1696                _ = [ float(a) for a in vals ]
1697                return float
1698            except:
1699                return lambda x: x
1700
1701    all_values = defaultdict(set)
1702    for a in [ at.attributes for at in data.domain.attributes ]:
1703        for k,v in a.iteritems():
1704            all_values[k].add(v)
1705
1706    types = {}
1707    for k,vals in all_values.iteritems():
1708        types[k] = data_type(vals)
1709
1710    for group, elements in d.items():
1711        a = orange.FloatVariable()
1712        #here sort elements
1713   
1714        def sk(x):
1715            return [ types[n](x[n]) for n in ignorenames if n in all_values ]
1716
1717        elements = sorted(elements, key=lambda x: sk(data.domain.attributes[x].attributes))
1718
1719        a.attributes.update(join_ats([data.domain.attributes[i].attributes for i in elements]))
1720        a.name = namefn(a.attributes)
1721
1722        def avgel(ex, el):
1723            return orange.Value(avgnone([ nativeOrNone(ex[ind]) for ind in el ]))
1724
1725        a.getValueFrom = lambda ex,rw,el=elements: avgel(ex,el)
1726        natts.append(a)
1727
1728    ndom = orange.Domain(natts, data.domain.classVar)
1729    ndom.addmetas(data.domain.getmetas())
1730    return orange.ExampleTable(ndom, data)
1731
1732
1733class DictyBase(object):
1734
1735    domain = "dictybase"
1736    filename = "information_mappings.pck"
1737    tags = [ "Dictyostelium discoideum", "gene", "essential", "dictyBase" ] 
1738 
1739    @classmethod
1740    def version(cls):
1741        orngServerFiles.localpath_download(cls.domain, cls.filename)
1742        return orngServerFiles.info(cls.domain, cls.filename)["datetime"]
1743   
1744    @classmethod
1745    def download_information(cls):
1746        """
1747        Downloads gene information and parses it.
1748        Returns a dictionary {ID: (name, synonyms, products)}
1749        """
1750        s = download_url("http://www.dictybase.org/db/cgi-bin/dictyBase/download/download.pl?area=general&ID=gene_information.txt").read()
1751        out = []
1752        for l in txt2ll(s, separ='\t', lineSepar='\n')[1:]:
1753            if len(l) == 4:
1754                id = l[0]
1755                name = l[1]
1756                synonyms = filter(None, l[2].split(", "))
1757                products = l[3]
1758                out.append((id, name, synonyms, products))
1759        return dict((a,(b,c,d)) for a,b,c,d in out)
1760
1761    @classmethod
1762    def download_mappings(cls):
1763        """
1764        Downloads DDB-GeneID-UniProt mappings and parses them.
1765        Returns a list of (ddb, ddb_g, uniprot) triplets.
1766       
1767        2009/04/07: ddb's appear unique
1768        """
1769        s = download_url("http://www.dictybase.org/db/cgi-bin/dictyBase/download/download.pl?area=general&ID=DDB-GeneID-UniProt.txt").read()
1770        out = []
1771        for l in txt2ll(s, separ='\t', lineSepar='\n')[1:]:
1772            if len(l) == 4:
1773                ddb = empty_none(l[0])
1774                ddb_g = empty_none(l[1])
1775                name = empty_none(l[2])
1776                uniprot = empty_none(l[3])
1777                out.append((ddb, ddb_g, name, uniprot))
1778        return out
1779
1780    @classmethod
1781    def pickle_data(cls):
1782        info = cls.download_information()
1783        mappings = cls.download_mappings()
1784        return pickle.dumps((info,mappings), -1)
1785
1786    def __init__(self):
1787        fn = orngServerFiles.localpath_download(self.domain, self.filename)
1788        self.info, self.mappings = pickle.load(open(fn, 'rb'))
1789
1790if __name__=="__main__":
1791    verbose = 1
1792
1793    def printet(et):
1794        et.save("ett.tab")
1795        print open("ett.tab").read()
1796
1797    """
1798    a = DictyBase()
1799    print len(a.info)
1800
1801    dbc = DictyExpress(buffer=BufferSQLite("../tmpbufnew"))
1802
1803    print dbc.annotationOptions()
1804
1805    count = 0
1806    def cb():
1807        global count
1808        count += 1
1809        #print "CBBB", count
1810
1811    et = dbc.get_single_data(sample=[ "tagA-", "pkaC-"], callback=cb, exclude_constant_labels=True, allowed_labels=["sample"])
1812    print et.domain
1813    print et.domain[0].attributes
1814    printet(et)
1815    """
1816
1817    d = PIPA(buffer=BufferSQLite("../tmpbufnewpipa"))
1818
1819    print d.gene_expression_types()
1820
1821    cov = d.coverage("777", chromosome=1, genome="dd")
1822    print cov
1823
1824    allids = d.annotations().keys()
1825    allids = allids[:1]
1826    print ("list", allids)
1827    print d.annotations().items()[0]
1828    print ("annots", d.annotations().items()[:2])
1829
1830    allids = map(str, allids)
1831
1832    import math
1833
1834    data = d.get_data(ids=allids, ctype="3")
1835
1836    #data = d.get_data(ids=allids, transform=lambda x: math.log(x+1, 2), allowed_labels=["strain"], ctype="3")
1837
1838    printet(data)
Note: See TracBrowser for help on using the repository browser.