source: orange-bioinformatics/_bioinformatics/obiKEGG2/databases.py @ 1636:10d234fdadb9

Revision 1636:10d234fdadb9, 13.9 KB checked in by mitar, 2 years ago (diff)

Restructuring because we will not be using namespaces.

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              ("KEYWORDS", fields.DBSimpleField),
193              ("DISEASE", fields.DBSimpleField),
194              ("COMMENT", fields.DBSimpleField),
195              ("CHROMOSOME", fields.DBFieldWithSubsections),
196              ("STATISTICS", fields.DBSimpleField),
197              ("REFERENCE", fields.DBReference)]
198   
199    MULTIPLE_FIELDS = ["REFERENCE"]
200   
201    def __init__(self, text):
202        entry.DBEntry.__init__(self, text)
203       
204    @property
205    def entry_key(self):
206        """ Primary entry key used for querying.
207       
208        .. note:: Unlike most of the other entry types this is the
209            first listed 'NAME'.
210           
211        """
212       
213        return self.name.split(",", 1)[0]
214
215    @property
216    def taxid(self):
217        return self.TAXONOMY.taxid
218           
219    def org_code(self):
220        if self.name is not None:
221            return self.name.split(",")[0]
222        else:
223            return self.entry.split(" ")[0]
224       
225
226class Genome(DBDataBase):
227    DB = "genome"
228    ENTRY_TYPE = GenomeEntry
229   
230    # For obiTaxonomy.common_taxids mapping
231    TAXID_MAP = {"562": "511145",   # Escherichia coli K-12 MG1655
232                 "2104": "272634",  # Mycoplasma pneumoniae M129
233                 "4530": "39947",   # Oryza sativa ssp. japonica cultivar Nipponbare (Japanese rice)
234                 "4932" : "559292", # Saccharomyces cerevisiae S288C
235                 "4896": "284812",  # Schizosaccharomyces pombe 972h-
236                 }
237   
238    def __init__(self):
239        DBDataBase.__init__(self)
240        self._keys = [org.entry_id for org in self.api.list_organisms()]
241   
242    def _key_to_gn_entry_id(self, key):
243        res = self.find(key)
244        if len(res) == 0:
245            raise KeyError("Unknown key")
246        elif len(res) > 1:
247            raise ValueError("Not a unique key")
248        else:
249            return res[0]
250   
251    @classmethod
252    def common_organisms(cls):
253        return ['ath', 'bta', 'cel', 'cre', 'dre', 'ddi',
254                'dme', 'eco', 'hsa', 'mmu', 'mpn', 'osa',
255                'pfa', 'rno', 'sce', 'spo', 'zma', 'xla']
256       
257    @classmethod
258    def essential_organisms(cls):
259        return ['ddi', 'dme', 'hsa', 'mmu', 'sce']
260   
261    def search(self, string, relevance=False):
262        """ Search the genome database for string using ``bfind``.
263        """
264        if relevance:
265            raise NotImplementedError("relevance is no longer supported")
266        if string in self.TAXID_MAP:
267            string = self.TAXID_MAP[string]
268           
269        res = self.api.bfind(self.DB, string)
270        if not res:
271            return []
272       
273        res = res.splitlines()
274        res = [r.split(",", 1)[0] for r in res]
275        res = [r.split(" ", 1)[1] for r in res]
276        return res
277   
278   
279@entry.entry_decorate
280class GeneEntry(entry.DBEntry):
281    FIELDS = [("ENTRY", fields.DBEntryField),
282              ("NAME", fields.DBNameField),
283              ("DEFINITION", fields.DBDefinitionField),
284              ("ORGANISM", fields.DBSimpleField),
285              ("ORTHOLOGY", fields.DBSimpleField),
286              ("DRUG_TARGET", fields.DBSimpleField),
287              ("PATHWAY", fields.DBPathway),
288              ("MODULE", fields.DBSimpleField),
289              ("DISEASE", fields.DBSimpleField),
290              ("CLASS", fields.DBSimpleField),
291              ("POSITION", fields.DBSimpleField),
292              ("MOTIF", fields.DBSimpleField),
293              ("DBLINKS", fields.DBDBLinks),
294              ("STRUCTURE", fields.DBSimpleField),
295              ("AASEQ", fields.DBAASeq),
296              ("NTSEQ", fields.DBNTSeq)]
297   
298    def aliases(self):
299        return [self.entry_key] + (self.name.split(",") if self.name else []) + [link[1][0] for link in self.dblinks.items() if self.dblinks]
300
301    @property
302    def alt_names(self):
303        """ For backwards compatibility.
304        """
305        return self.aliases()
306 
307class Genes(DBDataBase):
308    DB = None # Needs to be set in __init__
309    ENTRY_TYPE = GeneEntry
310   
311    def __init__(self, org_code):
312        self.DB = org_code
313        self.org_code = org_code
314        DBDataBase.__init__(self)
315        self._keys = self.api.get_genes_by_organism(org_code)
316       
317    def gene_aliases(self):
318        aliases = {}
319        for entry in self.itervalues():
320            aliases.update(dict.fromkeys(entry.aliases(), self.org_code + ":" + entry.entry_key()))
321        return aliases
322   
323
324@entry.entry_decorate
325class CompoundEntry(entry.DBEntry):
326    FIELDS = [("ENTRY", fields.DBEntryField),
327              ("NAME", fields.DBNameField),
328              ("FORMULA", fields.DBSimpleField),
329              ("MASS", fields.DBSimpleField),
330              ("REMARK", fields.DBSimpleField),
331              ("REACTION", fields.DBSimpleField),
332              ("PATHWAY", fields.DBPathway),
333              ("ENZYME", fields.DBSimpleField),
334              ("DBLINKS", fields.DBDBLinks),
335              ("ATOM", fields.DBSimpleField),
336              ("BOND", fields.DBSimpleField)
337              ]
338   
339   
340class Compounds(DBDataBase):
341    DB = "cpd"
342    ENTRY_TYPE = CompoundEntry
343   
344    def __init__(self):
345        DBDataBase.__init__(self)
346        self._keys = [] # All keys are not available
347
348
349@entry.entry_decorate   
350class ReactionEntry(entry.DBEntry):
351    FIELDS = [("ENTRY", fields.DBEntryField),
352              ("NAME", fields.DBNameField),
353              ("DEFINITION", fields.DBDefinitionField),
354              ("EQUATION", fields.DBSimpleField),
355              ("ENZYME", fields.DBSimpleField)
356              ]
357   
358class Reactions(DBDataBase):
359    DB = "rn"
360    ENTRY_TYPE = ReactionEntry
361   
362    def __init__(self):
363        DBDataBase.__init__(self)
364        self._keys = [] # All keys are not available
365         
366class Brite(DBDataBase):
367    DB = "br"
368   
369class Disease(DBDataBase):
370    DB = "ds"
371       
372class Drug(DBDataBase):
373    DB = "dr"
374   
375@entry.entry_decorate
376class EnzymeEntry(entry.DBEntry):
377    FIELDS = [("ENTRY", fields.DBEntryField),
378              ("NAME", fields.DBNameField),
379              ("CLASS", fields.DBSimpleField),
380              ("SYSNAME", fields.DBSimpleField),
381              ("REACTION", fields.DBSimpleField),
382              ("ALL_REAC", fields.DBSimpleField),
383              ("SUBSTRATE", fields.DBSimpleField),
384              ("PRODUCT", fields.DBSimpleField),
385              ("COMMENT", fields.DBSimpleField),
386              ("REFERENCE", fields.DBReference),
387              ("PATHWAY", fields.DBPathway),
388              ("ORTHOLOGY", fields.DBSimpleField),
389              ("GENES", fields.DBSimpleField),
390              ("DBLINKS", fields.DBDBLinks)
391              ]
392   
393    MULTIPLE_FIELDS = ["REFERENCE"]
394   
395class Enzymes(DBDataBase):
396    DB = "ec"
397    ENTRY_TYPE = EnzymeEntry
398   
399   
400@entry.entry_decorate
401class OrthologyEntry(entry.DBEntry):
402    FIELDS = [("ENTRY", fields.DBEntryField),
403              ("NAME", fields.DBNameField),
404              ("CLASS", fields.DBSimpleField),
405              ("DBLINKS", fields.DBDBLinks),
406              ("GENES", fields.DBSimpleField),
407              ]
408   
409class Orthology(DBDataBase):
410    DB = "ko"
411    ENTRY_TYPE = OrthologyEntry
412   
413   
414@entry.entry_decorate
415class PathwayEntry(entry.DBEntry):
416    FIELDS = [("ENTRY", fields.DBEntryField),
417              ("NAME", fields.DBNameField),
418              ("DESCRIPTION", fields.DBSimpleField),
419              ("CLASS", fields.DBSimpleField),
420              ("PATHWAY_MAP", fields.DBPathwayMapField),
421              ("DISEASE", fields.DBSimpleField),
422              ("DRUG", fields.DBSimpleField),
423              ("DBLINKS", fields.DBDBLinks),
424              ("ORGANISM", fields.DBSimpleField),
425              ("GENE", fields.DBGeneField),
426              ("ENZYME", fields.DBEnzymeField),
427              ("COMPOUND", fields.DBCompoundField),
428              ("REFERENCE", fields.DBReference),
429              ("REL_PATHWAY", fields.DBSimpleField),
430              ("KO_PATHWAY", fields.DBSimpleField),
431              ]
432   
433    MULTIPLE_FIELDS = ["REFERENCE"]
434   
435    @property
436    def gene(self):
437        if hasattr(self, "GENE"):
438            genes = self.GENE._convert()
439        else:
440            return None
441       
442        org = self.organism
443        org_prefix = ""
444        if org:
445            match = re.findall(r"\[GN:([a-z]+)\]", org)
446            if match:
447                org_prefix = match[0] + ":"
448        genes = [org_prefix + g for g in genes]
449        return genes
450   
451class Pathways(DBDataBase):
452    DB = "path"
453    ENTRY_TYPE = PathwayEntry
454   
455    def __init__(self):
456        DBDataBase.__init__(self)
457   
Note: See TracBrowser for help on using the repository browser.