source: orange-bioinformatics/obiKEGG2/databases.py @ 1538:1241665d4e60

Revision 1538:1241665d4e60, 12.8 KB checked in by ales_erjavec, 2 years ago (diff)

Fixing errors in get and batch_get.

Line 
1"""
2DBGET database
3"""
4from __future__ import absolute_import
5
6import re
7
8from . import entry
9from .entry import fields
10from . import api
11
12def iter_take(source_iter, n):
13    source_iter = iter(source_iter)
14    return [item for _, item in zip(range(n), source_iter)]
15
16def batch_iter(source_iter, n):
17    source_iter = iter(source_iter)
18    while True:
19        batch = iter_take(source_iter, n)
20        if batch:
21            yield batch
22        else:
23            break
24       
25def chain_iter(chains_iter):
26    for iter in chains_iter:
27        for element in iter:
28            yield element
29
30class DBDataBase(object):
31    """ A wrapper for DBGET database.
32    """
33    # ENTRY_TYPE constructor (type)
34    ENTRY_TYPE = entry.DBEntry
35   
36    # Needs to be set in a subclass or object instance
37    DB = None
38   
39    def __init__(self, **kwargs):
40        if not self.DB:
41            raise TypeError("Cannot make an instance of abstract base class %r." \
42                            % type(self).__name__)
43           
44        self.api = api.CachedKeggApi()
45        self.info = self.api.binfo(self.DB)
46        release = self.info.release
47        self.api.set_default_release(release)
48        self._keys = []
49       
50    def keys(self):
51        return list(self._keys)
52   
53    def iterkeys(self):
54        return iter(self._keys)
55   
56    def items(self):
57        return list(zip(self.keys(), self.batch_get(self.keys())))
58   
59    def iteritems(self):
60        batch_size = 100
61        iterkeys = self.iterkeys()
62        return chain_iter(zip(batch, self.batch_get(batch))
63                          for batch in batch_iter(iterkeys, batch_size))
64       
65#        return ((key, self.__getitem__(key)) for key in self.iterkeys())
66   
67    def values(self):
68        return self.batch_get(self.keys())
69   
70    def itervalues(self):
71        batch_size = 100
72        iterkeys = self.iterkeys()
73        return chain_iter(self.batch_get(batch)
74                          for batch in batch_iter(iterkeys, batch_size))
75       
76#        return (self.__getitem__(key) for key in self.iterkeys())
77   
78    def get(self, key, default=None):
79        try:
80            return self.__getitem__(key)
81        except KeyError:
82            return default
83       
84    def has_key(self, key):
85        return self.__contains__(key)
86   
87    def __getitem__(self, key):
88        e = self.get_entry(key)
89        if e is None:
90            raise KeyError(key)
91        else:
92            return e
93   
94    def __contains__(self, key):
95        return key in set(self.keys())
96   
97    def __len__(self):
98        return len(self.keys())
99   
100    def __iter__(self):
101        return iter(self.keys())
102   
103    def get_text(self, key):
104        key = self._add_db(key)
105        return self.api.bget([key])
106   
107    def get_entry(self, key):
108        text = self.get_text(key)
109        if not text or text == "None":
110            return None
111        else:
112            return self.ENTRY_TYPE(text)
113       
114    def find(self, name):
115        """ Find ``name`` using BFIND.
116        """
117        res = self.api.bfind(self.DB, name).splitlines()
118        return [r.split(" ", 1)[0] for r in res]   
119       
120    def pre_cache(self, keys=None, batch_size=100, progress_callback=None):
121        """ Retrive all the entries and cache them locally.
122        """
123        # TODO do this in multiple threads
124   
125        if not isinstance(self.api, api.CachedKeggApi):
126            raise TypeError("Not an an instance of api.CachedKeggApi")
127       
128        if batch_size > 100 or batch_size < 1:
129            raise ValueError("Invalid batch_size")
130       
131        if keys is None:
132            keys = self.keys()
133           
134        keys = list(keys)
135        start = 0
136        while start < len(keys):
137            batch = keys[start: start + batch_size]
138            batch = map(self._add_db, batch)
139           
140            self.api.bget(batch)
141           
142            if progress_callback:
143                progress_callback(100.0 * start / len(keys))
144               
145            start += batch_size
146           
147    def batch_get(self, keys):
148        """ Batch retrieve all entries for keys. This can be
149        significantly faster then getting each entry separately
150        especially if entries are not yet cached.
151       
152        """
153        entries = []
154        batch_size = 100
155        keys = list(keys)
156        start = 0
157        while start < len(keys):
158            batch = keys[start: start + batch_size]
159            batch = map(self._add_db, batch)
160            batch_entries = self.api.bget(batch)
161            if batch_entries is not None:
162                batch_entries = batch_entries.split("///\n")
163                # Remove possible empty last line 
164                batch_entries = [e for e in batch_entries if e.strip()]
165                entries.extend(map(self.ENTRY_TYPE, batch_entries))
166            start += batch_size
167           
168        return entries
169           
170    def _add_db(self, key):
171        """ Prefix the key with '%(DB)s:' string if not already
172        prefixed.
173        """
174        if not key.startswith(self.DB + ":"):
175            return self.DB + ":" + key
176        else:
177            return key
178       
179    @property
180    def entries(self):
181        return self.values()
182   
183@entry.entry_decorate
184class GenomeEntry(entry.DBEntry):
185    FIELDS = [("ENTRY", fields.DBEntryField),
186              ("NAME", fields.DBNameField),
187              ("DEFINITION", fields.DBDefinitionField),
188              ("ANNOTATION", fields.DBSimpleField),
189              ("TAXONOMY", fields.DBTaxonomyField),
190              ("DATA_SOURCE", fields.DBSimpleField),
191              ("ORIGINAL_DB", fields.DBSimpleField),
192              ("CHROMOSOME", fields.DBFieldWithSubsections),
193              ("STATISTICS", fields.DBSimpleField),
194              ("REFERENCE", fields.DBReference)]
195   
196    MULTIPLE_FIELDS = ["REFERENCE"]
197   
198    def __init__(self, text):
199        entry.DBEntry.__init__(self, text)
200       
201    @property
202    def entry_key(self):
203        """ Primary entry key used for querying.
204       
205        .. note:: Unlike most of the other entry types this is the
206            first listed 'NAME'.
207           
208        """
209       
210        return self.name.split(",", 1)[0]
211
212    @property
213    def taxid(self):
214        return self.TAXONOMY.taxid
215           
216    def org_code(self):
217        if self.name is not None:
218            return self.name.split(",")[0]
219        else:
220            return self.entry.split(" ")[0]
221       
222
223class Genome(DBDataBase):
224    DB = "genome"
225    ENTRY_TYPE = GenomeEntry
226   
227    # For obiTaxonomy.common_taxids mapping
228    TAXID_MAP = {"562": "511145",   # Escherichia coli K-12 MG1655
229                 "2104": "272634",  # Mycoplasma pneumoniae M129
230                 "4530": "39947",   # Oryza sativa ssp. japonica cultivar Nipponbare (Japanese rice)
231                 "4932" : "559292", # Saccharomyces cerevisiae S288C
232                 "4896": "284812",  # Schizosaccharomyces pombe 972h-
233                 }
234   
235    def __init__(self):
236        DBDataBase.__init__(self)
237        self._keys = [org.entry_id for org in self.api.list_organisms()]
238   
239    def _key_to_gn_entry_id(self, key):
240        res = self.find(key)
241        if len(res) == 0:
242            raise KeyError("Unknown key")
243        elif len(res) > 1:
244            raise ValueError("Not a unique key")
245        else:
246            return res[0]
247   
248    @classmethod
249    def common_organisms(cls):
250        return ['ath', 'bta', 'cel', 'cre', 'dre', 'ddi',
251                'dme', 'eco', 'hsa', 'mmu', 'mpn', 'osa',
252                'pfa', 'rno', 'sce', 'spo', 'zma', 'xla']
253       
254    @classmethod
255    def essential_organisms(cls):
256        return ['ddi', 'dme', 'hsa', 'mmu', 'sce']
257   
258    def search(self, string, relevance=False):
259        if relevance:
260            raise NotImplementedError("relevance is no longer supported")
261        if string in self.TAXID_MAP:
262            string = self.TAXID_MAP[string]
263           
264        res = self.api.bfind(self.DB, string)
265        if not res:
266            return []
267       
268        res = res.splitlines()
269        res = [r.split(",", 1)[0] for r in res]
270        res = [r.split(" ", 1)[1] for r in res]
271        return res
272   
273@entry.entry_decorate
274class GeneEntry(entry.DBEntry):
275    FIELDS = [("ENTRY", fields.DBEntryField),
276              ("NAME", fields.DBNameField),
277              ("DEFINITION", fields.DBDefinitionField),
278              ("ORTHOLOGY", fields.DBSimpleField),
279              ("DRUG_TARGET", fields.DBSimpleField),
280              ("PATHWAY", fields.DBPathway),
281              ("MODULE", fields.DBSimpleField),
282              ("DISEASE", fields.DBSimpleField),
283              ("CLASS", fields.DBSimpleField),
284              ("POSITION", fields.DBSimpleField),
285              ("MOTIF", fields.DBSimpleField),
286              ("DBLINKS", fields.DBDBLinks),
287              ("STRUCTURE", fields.DBSimpleField),
288              ("AASEQ", fields.DBAASeq),
289              ("NTSEQ", fields.DBNTSeq)]
290   
291    def aliases(self):
292        return [self.entry_key] + (self.name.split(",") if self.name else []) + [link[1][0] for link in self.dblinks.items() if self.dblinks]
293
294    @property
295    def alt_names(self):
296        """ For backwards compatibility.
297        """
298        return self.aliases()
299 
300class Genes(DBDataBase):
301    DB = None # Needs to be set in __init__
302    ENTRY_TYPE = GeneEntry
303   
304    def __init__(self, org_code):
305        self.DB = org_code
306        self.org_code = org_code
307        DBDataBase.__init__(self)
308        self._keys = self.api.get_genes_by_organism(org_code)
309       
310    def gene_aliases(self):
311        aliases = {}
312        for entry in self.itervalues():
313            aliases.update(dict.fromkeys(entry.aliases(), self.org_code + ":" + entry.entry_key()))
314        return aliases
315   
316
317@entry.entry_decorate
318class CompoundEntry(entry.DBEntry):
319    FIELDS = [("ENTRY", fields.DBEntryField),
320              ("NAME", fields.DBNameField),
321              ("FORMULA", fields.DBSimpleField),
322              ("MASS", fields.DBSimpleField),
323              ("REMARK", fields.DBSimpleField),
324              ("REACTION", fields.DBSimpleField),
325              ("PATHWAY", fields.DBPathway),
326              ("ENZYME", fields.DBSimpleField),
327              ("DBLINKS", fields.DBDBLinks),
328              ("ATOM", fields.DBSimpleField),
329              ("BOND", fields.DBSimpleField)
330              ]
331   
332   
333class Compounds(DBDataBase):
334    DB = "cpd"
335    ENTRY_TYPE = CompoundEntry
336   
337    def __init__(self):
338        DBDataBase.__init__(self)
339        self._keys = [] # All keys are not available
340
341KEGGCompounds = Compounds
342
343@entry.entry_decorate   
344class ReactionEntry(entry.DBEntry):
345    FIELDS = [("ENTRY", fields.DBEntryField),
346              ("NAME", fields.DBNameField),
347              ("DEFINITION", fields.DBDefinitionField),
348              ("EQUATION", fields.DBSimpleField),
349              ("ENZYME", fields.DBSimpleField)
350              ]
351   
352class Reactions(DBDataBase):
353    DB = "rn"
354    ENTRY_TYPE = ReactionEntry
355   
356    def __init__(self):
357        DBDataBase.__init__(self)
358        self._keys = [] # All keys are not available
359         
360class Brite(DBDataBase):
361    DB = "br"
362   
363class Disease(DBDataBase):
364    DB = "ds"
365       
366class Drug(DBDataBase):
367    DB = "dr"
368   
369class Enzymes(DBDataBase):
370    DB = "ec"
371   
372@entry.entry_decorate
373class OrthologyEntry(entry.DBEntry):
374    FIELDS = [("ENTRY", fields.DBEntryField),
375              ("NAME", fields.DBNameField),
376              ("CLASS", fields.DBSimpleField),
377              ("DBLINKS", fields.DBDBLinks),
378              ("GENES", fields.DBSimpleField),
379              ]
380   
381class Orthology(DBDataBase):
382    DB = "ko"
383    ENTRY_TYPE = OrthologyEntry
384   
385@entry.entry_decorate
386class PathwayEntry(entry.DBEntry):
387    FIELDS = [("ENTRY", fields.DBEntryField),
388              ("NAME", fields.DBNameField),
389              ("DESCRIPTION", fields.DBSimpleField),
390              ("CLASS", fields.DBSimpleField),
391              ("PATHWAY_MAP", fields.DBPathwayMapField),
392              ("DISEASE", fields.DBSimpleField),
393              ("DRUG", fields.DBSimpleField),
394              ("DBLINKS", fields.DBDBLinks),
395              ("ORGANISM", fields.DBSimpleField),
396              ("GENE", fields.DBGeneField),
397              ("ENZYME", fields.DBSimpleField),
398              ("COMPOUND", fields.DBSimpleField),
399              ("REFERENCE", fields.DBReference),
400              ("REL_PATHWAY", fields.DBSimpleField),
401              ("KO_PATHWAY", fields.DBSimpleField),
402              ]
403   
404    MULTIPLE_FIELDS = ["REFERENCE"]
405   
406    @property
407    def gene(self):
408        if hasattr(self, "GENE"):
409            genes = self.GENE._convert()
410        else:
411            return None
412       
413        org = self.organism
414        org_prefix = ""
415        if org:
416            match = re.findall(r"\[GN:([a-z]+)\]", org)
417            if match:
418                org_prefix = match[0] + ":"
419        genes = [org_prefix + g for g in genes]
420        return genes
421   
422class Pathways(DBDataBase):
423    DB = "path"
424    ENTRY_TYPE = PathwayEntry
425   
426    def __init__(self):
427        DBDataBase.__init__(self)
428   
Note: See TracBrowser for help on using the repository browser.