source: orange-bioinformatics/obiKEGG2/databases.py @ 1546:ddd6bfae87e9

Revision 1546:ddd6bfae87e9, 13.8 KB checked in by ales_erjavec, 2 years ago (diff)

Added EnzymeEntry class.

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              ("ORTHOLOGY", fields.DBSimpleField),
285              ("DRUG_TARGET", fields.DBSimpleField),
286              ("PATHWAY", fields.DBPathway),
287              ("MODULE", fields.DBSimpleField),
288              ("DISEASE", fields.DBSimpleField),
289              ("CLASS", fields.DBSimpleField),
290              ("POSITION", fields.DBSimpleField),
291              ("MOTIF", fields.DBSimpleField),
292              ("DBLINKS", fields.DBDBLinks),
293              ("STRUCTURE", fields.DBSimpleField),
294              ("AASEQ", fields.DBAASeq),
295              ("NTSEQ", fields.DBNTSeq)]
296   
297    def aliases(self):
298        return [self.entry_key] + (self.name.split(",") if self.name else []) + [link[1][0] for link in self.dblinks.items() if self.dblinks]
299
300    @property
301    def alt_names(self):
302        """ For backwards compatibility.
303        """
304        return self.aliases()
305 
306class Genes(DBDataBase):
307    DB = None # Needs to be set in __init__
308    ENTRY_TYPE = GeneEntry
309   
310    def __init__(self, org_code):
311        self.DB = org_code
312        self.org_code = org_code
313        DBDataBase.__init__(self)
314        self._keys = self.api.get_genes_by_organism(org_code)
315       
316    def gene_aliases(self):
317        aliases = {}
318        for entry in self.itervalues():
319            aliases.update(dict.fromkeys(entry.aliases(), self.org_code + ":" + entry.entry_key()))
320        return aliases
321   
322
323@entry.entry_decorate
324class CompoundEntry(entry.DBEntry):
325    FIELDS = [("ENTRY", fields.DBEntryField),
326              ("NAME", fields.DBNameField),
327              ("FORMULA", fields.DBSimpleField),
328              ("MASS", fields.DBSimpleField),
329              ("REMARK", fields.DBSimpleField),
330              ("REACTION", fields.DBSimpleField),
331              ("PATHWAY", fields.DBPathway),
332              ("ENZYME", fields.DBSimpleField),
333              ("DBLINKS", fields.DBDBLinks),
334              ("ATOM", fields.DBSimpleField),
335              ("BOND", fields.DBSimpleField)
336              ]
337   
338   
339class Compounds(DBDataBase):
340    DB = "cpd"
341    ENTRY_TYPE = CompoundEntry
342   
343    def __init__(self):
344        DBDataBase.__init__(self)
345        self._keys = [] # All keys are not available
346
347
348@entry.entry_decorate   
349class ReactionEntry(entry.DBEntry):
350    FIELDS = [("ENTRY", fields.DBEntryField),
351              ("NAME", fields.DBNameField),
352              ("DEFINITION", fields.DBDefinitionField),
353              ("EQUATION", fields.DBSimpleField),
354              ("ENZYME", fields.DBSimpleField)
355              ]
356   
357class Reactions(DBDataBase):
358    DB = "rn"
359    ENTRY_TYPE = ReactionEntry
360   
361    def __init__(self):
362        DBDataBase.__init__(self)
363        self._keys = [] # All keys are not available
364         
365class Brite(DBDataBase):
366    DB = "br"
367   
368class Disease(DBDataBase):
369    DB = "ds"
370       
371class Drug(DBDataBase):
372    DB = "dr"
373   
374@entry.entry_decorate
375class EnzymeEntry(entry.DBEntry):
376    FIELDS = [("ENTRY", fields.DBEntryField),
377              ("NAME", fields.DBNameField),
378              ("CLASS", fields.DBSimpleField),
379              ("SYSNAME", fields.DBSimpleField),
380              ("REACTION", fields.DBSimpleField),
381              ("ALL_REAC", fields.DBSimpleField),
382              ("SUBSTRATE", fields.DBSimpleField),
383              ("PRODUCT", fields.DBSimpleField),
384              ("COMMENT", fields.DBSimpleField),
385              ("REFERENCE", fields.DBReference),
386              ("PATHWAY", fields.DBPathway),
387              ("ORTHOLOGY", fields.DBSimpleField),
388              ("GENES", fields.DBSimpleField),
389              ("DBLINKS", fields.DBDBLinks)
390              ]
391   
392    MULTIPLE_FIELDS = ["REFERENCE"]
393   
394class Enzymes(DBDataBase):
395    DB = "ec"
396    ENTRY_TYPE = EnzymeEntry
397   
398   
399@entry.entry_decorate
400class OrthologyEntry(entry.DBEntry):
401    FIELDS = [("ENTRY", fields.DBEntryField),
402              ("NAME", fields.DBNameField),
403              ("CLASS", fields.DBSimpleField),
404              ("DBLINKS", fields.DBDBLinks),
405              ("GENES", fields.DBSimpleField),
406              ]
407   
408class Orthology(DBDataBase):
409    DB = "ko"
410    ENTRY_TYPE = OrthologyEntry
411   
412   
413@entry.entry_decorate
414class PathwayEntry(entry.DBEntry):
415    FIELDS = [("ENTRY", fields.DBEntryField),
416              ("NAME", fields.DBNameField),
417              ("DESCRIPTION", fields.DBSimpleField),
418              ("CLASS", fields.DBSimpleField),
419              ("PATHWAY_MAP", fields.DBPathwayMapField),
420              ("DISEASE", fields.DBSimpleField),
421              ("DRUG", fields.DBSimpleField),
422              ("DBLINKS", fields.DBDBLinks),
423              ("ORGANISM", fields.DBSimpleField),
424              ("GENE", fields.DBGeneField),
425              ("ENZYME", fields.DBEnzymeField),
426              ("COMPOUND", fields.DBCompoundField),
427              ("REFERENCE", fields.DBReference),
428              ("REL_PATHWAY", fields.DBSimpleField),
429              ("KO_PATHWAY", fields.DBSimpleField),
430              ]
431   
432    MULTIPLE_FIELDS = ["REFERENCE"]
433   
434    @property
435    def gene(self):
436        if hasattr(self, "GENE"):
437            genes = self.GENE._convert()
438        else:
439            return None
440       
441        org = self.organism
442        org_prefix = ""
443        if org:
444            match = re.findall(r"\[GN:([a-z]+)\]", org)
445            if match:
446                org_prefix = match[0] + ":"
447        genes = [org_prefix + g for g in genes]
448        return genes
449   
450class Pathways(DBDataBase):
451    DB = "path"
452    ENTRY_TYPE = PathwayEntry
453   
454    def __init__(self):
455        DBDataBase.__init__(self)
456   
Note: See TracBrowser for help on using the repository browser.